2
* Copyright © 2015 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License version 3 as
6
* published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Authored by: Kevin DuBois <kevin.dubois@canonical.com>
19
#include "mir_toolkit/mir_client_library.h"
21
#include <GLES2/gl2.h>
35
Connection(char const* socket_file) :
36
connection(mir_connect_sync(socket_file, __PRETTY_FUNCTION__))
38
if (!mir_connection_is_valid(connection))
39
throw std::runtime_error(std::string("could not connect to server: ") +
40
mir_connection_get_error_message(connection));
44
mir_connection_release(connection);
46
operator MirConnection*() { return connection; }
47
Connection(Connection const&) = delete;
48
Connection& operator=(Connection const&) = delete;
50
MirConnection* connection;
56
Context(Connection& connection, MirSurface* surface, int swap_interval) :
57
native_display(reinterpret_cast<EGLNativeDisplayType>(
58
mir_connection_get_egl_native_display(connection))),
59
native_window(reinterpret_cast<EGLNativeWindowType>(
60
mir_buffer_stream_get_egl_native_window(mir_surface_get_buffer_stream(surface)))),
61
display(native_display),
62
config(chooseconfig(display.disp)),
63
surface(display.disp, config, native_window),
64
context(display.disp, config)
67
eglSwapInterval(display.disp, swap_interval);
71
if (eglMakeCurrent(display.disp, surface.surface, surface.surface, context.context) == EGL_FALSE)
72
throw(std::runtime_error("could not makecurrent"));
74
void release_current()
76
if (eglMakeCurrent(display.disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_FALSE)
77
throw(std::runtime_error("could not makecurrent"));
81
if (eglSwapBuffers(display.disp, surface.surface) == EGL_FALSE)
82
throw(std::runtime_error("could not swapbuffers"));
84
Context(Context const&) = delete;
85
Context& operator=(Context const&) = delete;
87
EGLConfig chooseconfig(EGLDisplay disp)
92
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
97
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
99
if (eglChooseConfig(disp, attribs, &egl_config, 1, &n) != EGL_TRUE || n != 1)
100
throw std::runtime_error("could not find egl config");
103
EGLNativeDisplayType native_display;
104
EGLNativeWindowType native_window;
107
Display(EGLNativeDisplayType native) :
108
disp(eglGetDisplay(native))
110
int major{0}, minor{0};
111
if (disp == EGL_NO_DISPLAY)
112
throw std::runtime_error("no egl display");
113
if (eglInitialize(disp, &major, &minor) != EGL_TRUE || major != 1 || minor != 4)
114
throw std::runtime_error("could not init egl");
125
Surface(EGLDisplay display, EGLConfig config, EGLNativeWindowType native_window) :
127
surface(eglCreateWindowSurface(disp, config, native_window, NULL))
129
if (surface == EGL_NO_SURFACE)
130
throw std::runtime_error("could not create egl surface");
133
if (eglGetCurrentSurface(EGL_DRAW) == surface)
134
eglMakeCurrent(disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
135
eglDestroySurface(disp, surface);
142
EglContext(EGLDisplay disp, EGLConfig config) :
144
context(eglCreateContext(disp, config, EGL_NO_CONTEXT, context_attribs))
146
if (context == EGL_NO_CONTEXT)
147
throw std::runtime_error("could not create egl context");
151
eglDestroyContext(disp, context);
153
EGLint context_attribs[3] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
162
RenderProgram(Context& context, unsigned int width, unsigned int height) :
163
vertex(&vtex_shader_src, GL_VERTEX_SHADER),
164
fragment(&frag_shader_src, GL_FRAGMENT_SHADER),
165
program(vertex, fragment)
167
float square_side = 400.0f;
168
float scale[2] {square_side/width, square_side/height};
169
glUseProgram(program.program);
170
vPositionAttr = glGetAttribLocation(program.program, "vPosition");
171
glVertexAttribPointer(vPositionAttr, 4, GL_FLOAT, GL_FALSE, 0, vertex_data);
172
posUniform = glGetUniformLocation(program.program, "pos");
173
glClearColor(0.1, 0.1, 0.4, 1.0); //light blue
174
glClear(GL_COLOR_BUFFER_BIT);
175
auto scaleUniform = glGetUniformLocation(program.program, "scale");
176
glUniform2fv(scaleUniform, 1, scale);
178
context.swapbuffers();
179
context.release_current();
182
void draw(float x, float y)
184
float pos[2] = {x, y};
185
glUseProgram(program.program);
186
glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT );
187
glUniform2fv(posUniform, 1, pos);
188
glEnableVertexAttribArray(vPositionAttr);
189
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
190
glDisableVertexAttribArray(vPositionAttr);
193
RenderProgram(RenderProgram const&) = delete;
194
RenderProgram& operator=(RenderProgram const&) = delete;
196
GLchar const*const frag_shader_src =
198
"precision mediump float;"
200
" gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);"
203
GLchar const*const vtex_shader_src =
205
"attribute vec4 vPosition;"
207
"uniform vec2 scale;"
209
" gl_Position = vec4(vPosition.xy * scale + pos, 0.0, 1.0);"
214
Shader(GLchar const* const* src, GLuint type) :
215
shader(glCreateShader(type))
217
glShaderSource(shader, 1, src, 0);
218
glCompileShader(shader);
222
glDeleteShader(shader);
228
Program(Shader& vertex, Shader& fragment) :
229
program(glCreateProgram())
231
glAttachShader(program, vertex.shader);
232
glAttachShader(program, fragment.shader);
233
glLinkProgram(program);
237
glDeleteProgram(program);
241
GLfloat vertex_data[16]
243
-1.0, -1.0f, 0.0f, 1.0f,
244
-1.0f, 1.0f, 0.0f, 1.0f,
245
1.0f, -1.0f, 0.0f, 1.0f,
246
1.0f, 1.0f, 0.0f, 1.0f,
248
GLuint vPositionAttr;
255
Surface(Connection& connection, int swap_interval) :
256
dimensions(active_output_dimensions(connection)),
257
surface{create_surface(connection, dimensions), surface_deleter},
258
context{connection, surface.get(), swap_interval},
259
program{context, dimensions.width, dimensions.height}
263
void on_event(MirEvent const* ev)
265
if (mir_event_get_type(ev) != mir_event_type_input)
269
auto ievent = mir_event_get_input_event(ev);
270
if (mir_input_event_get_type(ievent) == mir_input_event_type_touch)
272
auto tev = mir_input_event_get_touch_event(ievent);
273
x = mir_touch_event_axis_value(tev, 0, mir_touch_axis_x);
274
y = mir_touch_event_axis_value(tev, 0, mir_touch_axis_y);
276
else if (mir_input_event_get_type(ievent) == mir_input_event_type_pointer)
278
auto pev = mir_input_event_get_pointer_event(ievent);
279
x = mir_pointer_event_axis_value(pev, mir_pointer_axis_x);
280
y = mir_pointer_event_axis_value(pev, mir_pointer_axis_y);
286
context.make_current();
288
x/static_cast<float>(dimensions.width)*2.0 - 1.0,
289
y/static_cast<float>(dimensions.height)*-2.0 + 1.0);
290
context.swapbuffers();
293
Surface(Surface const&) = delete;
294
Surface& operator=(Surface const&) = delete;
296
struct OutputDimensions
298
unsigned int const width;
299
unsigned int const height;
303
std::function<void(MirSurface*)> const surface_deleter{
304
[](MirSurface* surface)
306
mir_surface_release_sync(surface);
309
std::unique_ptr<MirSurface, decltype(surface_deleter)> surface;
311
RenderProgram program;
313
OutputDimensions active_output_dimensions(MirConnection* connection)
315
unsigned int width{0};
316
unsigned int height{0};
317
auto display_config = mir_connection_create_display_config(connection);
318
for (auto i = 0u; i < display_config->num_outputs; i++)
320
MirDisplayOutput const* out = display_config->outputs + i;
324
out->current_mode < out->num_modes)
326
width = out->modes[out->current_mode].horizontal_resolution;
327
height = out->modes[out->current_mode].vertical_resolution;
331
mir_display_config_destroy(display_config);
332
if (width == 0 || height == 0)
333
throw std::logic_error("could not determine display size");
334
return {width, height};
337
MirSurface* create_surface(MirConnection* connection, OutputDimensions const& dim)
339
MirPixelFormat selected_format;
340
unsigned int valid_formats{0};
341
MirPixelFormat pixel_formats[mir_pixel_formats];
342
mir_connection_get_available_surface_formats(connection, pixel_formats, mir_pixel_formats, &valid_formats);
343
if (valid_formats == 0)
344
throw std::runtime_error("no pixel formats for surface");
345
selected_format = pixel_formats[0];
346
//select an 8 bit opaque format if we can
347
for(auto i = 0u; i < valid_formats; i++)
349
if (pixel_formats[i] == mir_pixel_format_xbgr_8888 ||
350
pixel_formats[i] == mir_pixel_format_xrgb_8888)
352
selected_format = pixel_formats[i];
357
auto deleter = [](MirSurfaceSpec *spec) { mir_surface_spec_release(spec); };
358
std::unique_ptr<MirSurfaceSpec, decltype(deleter)> spec{
359
mir_connection_create_spec_for_normal_surface(connection, dim.width, dim.height, selected_format),
363
mir_surface_spec_set_name(spec.get(), __PRETTY_FUNCTION__);
364
mir_surface_spec_set_buffer_usage(spec.get(), mir_buffer_usage_hardware);
365
auto surface = mir_surface_create_sync(spec.get());
366
mir_surface_set_event_handler(surface, &on_event, this);
369
static void on_event(MirSurface*, const MirEvent *event, void *context)
371
auto surface = reinterpret_cast<Surface*>(context);
372
if (surface) surface->on_event(event);
378
int main(int argc, char *argv[])
381
using namespace std::literals::chrono_literals;
384
char const* socket_file = nullptr;
385
int swap_interval = 1;
386
while ((arg = getopt (argc, argv, "nm:")) != -1)
391
socket_file = optarg;
397
throw std::invalid_argument("invalid command line argument");
401
Connection connection(socket_file);
402
Surface surface(connection, swap_interval);
406
sigemptyset(&sigset);
407
sigaddset(&sigset, SIGTERM);
408
sigaddset(&sigset, SIGINT);
409
sigprocmask(SIG_BLOCK, &sigset, nullptr);
410
sigwaitinfo(&sigset, &siginfo);
413
catch(std::exception& e)
415
std::cerr << "error : " << e.what() << std::endl;