forked from enlightenment/enlightenment
485 lines
14 KiB
C
485 lines
14 KiB
C
#include "e.h"
|
|
#include "e_mod_main.h"
|
|
#ifdef HAVE_WAYLAND
|
|
# include "e_mod_comp_wl.h"
|
|
# include "e_mod_comp_wl_comp.h"
|
|
# include "e_mod_comp_wl_output.h"
|
|
# include "e_mod_comp_wl_surface.h"
|
|
#endif
|
|
|
|
# define WL_OUTPUT_FLIPPED 0x01
|
|
|
|
/* local function prototypes */
|
|
static void _e_mod_comp_wl_output_bind(struct wl_client *client, void *data, uint32_t version __UNUSED__, uint32_t id);
|
|
static void _e_mod_comp_wl_output_scanout_buffer_destroy(struct wl_listener *listener, struct wl_resource *resource __UNUSED__, uint32_t timestamp __UNUSED__);
|
|
static void _e_mod_comp_wl_output_pending_scanout_buffer_destroy(struct wl_listener *listener, struct wl_resource *resource __UNUSED__, uint32_t timestamp __UNUSED__);
|
|
static int _e_mod_comp_wl_output_finish_frame_handler(void *data);
|
|
static void _e_mod_comp_wl_output_finish_frame(Wayland_Output *output, int secs);
|
|
static void _e_mod_comp_wl_output_repaint(Wayland_Output *output, int secs);
|
|
static void _e_mod_comp_wl_output_schedule_repaint(void);
|
|
static Eina_Bool _e_mod_comp_wl_output_setup_scanout_surface(Wayland_Output *output, Wayland_Surface *ws);
|
|
static int _e_mod_comp_wl_output_prepare_render(Wayland_Output *output);
|
|
static int _e_mod_comp_wl_output_present(Wayland_Output *output);
|
|
static int _e_mod_comp_wl_output_prepare_scanout_surface(Wayland_Output *output __UNUSED__, Wayland_Surface *surface __UNUSED__);
|
|
static void _e_mod_comp_wl_output_move(Wayland_Output *output, int32_t x, int32_t y);
|
|
|
|
/* private variables */
|
|
static Wayland_Output *_wl_output;
|
|
|
|
Eina_Bool
|
|
e_mod_comp_wl_output_init(void)
|
|
{
|
|
Wayland_Compositor *comp;
|
|
struct wl_event_loop *loop;
|
|
Ecore_X_Window *roots;
|
|
int num = 0, rw, rh;
|
|
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
roots = ecore_x_window_root_list(&num);
|
|
if ((!roots) || (num <= 0))
|
|
{
|
|
EINA_LOG_ERR("Could not get root window list\n");
|
|
return EINA_FALSE;
|
|
}
|
|
ecore_x_window_size_get(roots[0], &rw, &rh);
|
|
|
|
if (!(_wl_output = malloc(sizeof(Wayland_Output))))
|
|
{
|
|
EINA_LOG_ERR("Could not allocate space for output\n");
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
memset(_wl_output, 0, sizeof(*_wl_output));
|
|
|
|
_wl_output->mode.flags =
|
|
(WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED);
|
|
|
|
_wl_output->mode.width = rw;
|
|
_wl_output->mode.height = rh;
|
|
_wl_output->mode.refresh = 60;
|
|
|
|
// weston output init
|
|
|
|
_wl_output->x = 0;
|
|
_wl_output->y = 0;
|
|
_wl_output->width = rw;
|
|
_wl_output->height = rh;
|
|
_wl_output->flags = WL_OUTPUT_FLIPPED;
|
|
|
|
_e_mod_comp_wl_output_move(_wl_output, 0, 0);
|
|
|
|
_wl_output->scanout_buffer_destroy_listener.func =
|
|
_e_mod_comp_wl_output_scanout_buffer_destroy;
|
|
_wl_output->pending_scanout_buffer_destroy_listener.func =
|
|
_e_mod_comp_wl_output_pending_scanout_buffer_destroy;
|
|
|
|
wl_list_init(&_wl_output->link);
|
|
wl_list_init(&_wl_output->frame_callbacks);
|
|
|
|
if (!wl_display_add_global(_wl_disp, &wl_output_interface, _wl_output,
|
|
_e_mod_comp_wl_output_bind))
|
|
{
|
|
EINA_LOG_ERR("Failed to add wayland output\n");
|
|
free(_wl_output);
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
// NB: compositor-x11 has create output window
|
|
|
|
comp = e_mod_comp_wl_comp_get();
|
|
|
|
_wl_output->egl_surface =
|
|
eglCreateWindowSurface(comp->egl.display, comp->egl.config,
|
|
roots[0], NULL); // NB: roots[0] == output->window
|
|
free(roots);
|
|
|
|
if (!_wl_output->egl_surface)
|
|
{
|
|
EINA_LOG_ERR("Failed to create EGL Surface\n");
|
|
free(_wl_output);
|
|
return EINA_FALSE;
|
|
}
|
|
if (!eglMakeCurrent(comp->egl.display, _wl_output->egl_surface,
|
|
_wl_output->egl_surface, comp->egl.context))
|
|
{
|
|
EINA_LOG_ERR("Failed to make current\n");
|
|
free(_wl_output);
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
loop = wl_display_get_event_loop(_wl_disp);
|
|
_wl_output->finish_frame_timer =
|
|
wl_event_loop_add_timer(loop, _e_mod_comp_wl_output_finish_frame_handler,
|
|
_wl_output);
|
|
|
|
_wl_output->prepare_render = _e_mod_comp_wl_output_prepare_render;
|
|
_wl_output->present = _e_mod_comp_wl_output_present;
|
|
_wl_output->prepare_scanout_surface =
|
|
_e_mod_comp_wl_output_prepare_scanout_surface;
|
|
|
|
// TODO: Add output destroy function
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
Eina_Bool
|
|
e_mod_comp_wl_output_shutdown(void)
|
|
{
|
|
Wayland_Compositor *comp;
|
|
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
if (!_wl_output) return EINA_TRUE;
|
|
|
|
comp = e_mod_comp_wl_comp_get();
|
|
|
|
wl_list_remove(&_wl_output->frame_callbacks);
|
|
wl_list_remove(&_wl_output->link);
|
|
|
|
if (_wl_output->finish_frame_timer)
|
|
wl_event_source_remove(_wl_output->finish_frame_timer);
|
|
|
|
eglDestroySurface(comp->egl.display, _wl_output->egl_surface);
|
|
|
|
pixman_region32_fini(&_wl_output->region);
|
|
pixman_region32_fini(&_wl_output->prev_damage);
|
|
|
|
free(_wl_output);
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
void
|
|
e_mod_comp_wl_output_damage(Wayland_Output *output)
|
|
{
|
|
Wayland_Compositor *comp;
|
|
Wayland_Surface *ws;
|
|
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
comp = e_mod_comp_wl_comp_get();
|
|
if (wl_list_empty(&comp->surfaces)) return;
|
|
ws = container_of(comp->surfaces.next, Wayland_Surface, link);
|
|
pixman_region32_union(&ws->damage, &ws->damage, &output->region);
|
|
e_mod_comp_wl_comp_schedule_repaint();
|
|
}
|
|
|
|
Wayland_Output *
|
|
e_mod_comp_wl_output_get(void)
|
|
{
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
return _wl_output;
|
|
}
|
|
|
|
void
|
|
e_mod_comp_wl_output_idle_repaint(void *data)
|
|
{
|
|
Wayland_Output *output;
|
|
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
if (!(output = data)) return;
|
|
if (output->repaint_needed)
|
|
{
|
|
struct timeval tv;
|
|
uint32_t timestamp;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
timestamp = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
|
_e_mod_comp_wl_output_repaint(output, timestamp);
|
|
}
|
|
}
|
|
|
|
/* local functions */
|
|
static void
|
|
_e_mod_comp_wl_output_bind(struct wl_client *client, void *data, uint32_t version __UNUSED__, uint32_t id)
|
|
{
|
|
Wayland_Output *output;
|
|
struct wl_resource *resource;
|
|
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
if (!(output = data)) return;
|
|
|
|
resource =
|
|
wl_client_add_object(client, &wl_output_interface, NULL, id, data);
|
|
|
|
wl_resource_post_event(resource, WL_OUTPUT_GEOMETRY, output->x, output->y,
|
|
output->width, output->height, output->subpixel,
|
|
output->make, output->model);
|
|
|
|
wl_resource_post_event(resource, WL_OUTPUT_MODE, output->mode.flags,
|
|
output->mode.width, output->mode.height,
|
|
output->mode.refresh);
|
|
}
|
|
|
|
static void
|
|
_e_mod_comp_wl_output_scanout_buffer_destroy(struct wl_listener *listener, struct wl_resource *resource __UNUSED__, uint32_t timestamp __UNUSED__)
|
|
{
|
|
Wayland_Output *output;
|
|
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
output =
|
|
container_of(listener, Wayland_Output, scanout_buffer_destroy_listener);
|
|
|
|
output->scanout_buffer = NULL;
|
|
|
|
if (!output->pending_scanout_buffer)
|
|
e_mod_comp_wl_comp_schedule_repaint();
|
|
}
|
|
|
|
static void
|
|
_e_mod_comp_wl_output_pending_scanout_buffer_destroy(struct wl_listener *listener, struct wl_resource *resource __UNUSED__, uint32_t timestamp __UNUSED__)
|
|
{
|
|
Wayland_Output *output;
|
|
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
output =
|
|
container_of(listener, Wayland_Output,
|
|
pending_scanout_buffer_destroy_listener);
|
|
|
|
output->pending_scanout_buffer = NULL;
|
|
|
|
e_mod_comp_wl_comp_schedule_repaint();
|
|
}
|
|
|
|
static int
|
|
_e_mod_comp_wl_output_finish_frame_handler(void *data)
|
|
{
|
|
Wayland_Output *output;
|
|
uint32_t msec;
|
|
struct timeval tv;
|
|
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
if (!(output = data)) return 1;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
|
_e_mod_comp_wl_output_finish_frame(output, msec);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
_e_mod_comp_wl_output_finish_frame(Wayland_Output *output, int secs)
|
|
{
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
if (!output) return;
|
|
if (output->scanout_buffer)
|
|
{
|
|
e_mod_comp_wl_buffer_post_release(output->scanout_buffer);
|
|
wl_list_remove(&output->scanout_buffer_destroy_listener.link);
|
|
output->scanout_buffer = NULL;
|
|
}
|
|
if (output->pending_scanout_buffer)
|
|
{
|
|
output->scanout_buffer = output->pending_scanout_buffer;
|
|
wl_list_remove(&output->pending_scanout_buffer_destroy_listener.link);
|
|
wl_list_insert(output->scanout_buffer->resource.destroy_listener_list.prev,
|
|
&output->scanout_buffer_destroy_listener.link);
|
|
output->pending_scanout_buffer = NULL;
|
|
}
|
|
if (output->repaint_needed)
|
|
_e_mod_comp_wl_output_repaint(output, secs);
|
|
else
|
|
output->repaint_scheduled = EINA_FALSE;
|
|
}
|
|
|
|
static void
|
|
_e_mod_comp_wl_output_repaint(Wayland_Output *output, int secs)
|
|
{
|
|
Wayland_Frame_Callback *cb, *cnext;
|
|
Wayland_Compositor *comp;
|
|
Wayland_Surface *ws;
|
|
pixman_region32_t opaque, new_damage, total_damage, repaint;
|
|
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
if (!output) return;
|
|
|
|
comp = e_mod_comp_wl_comp_get();
|
|
|
|
/* start weston output repaint */
|
|
output->prepare_render(output);
|
|
|
|
glViewport(0, 0, output->width, output->height);
|
|
|
|
glUseProgram(comp->texture_shader.program);
|
|
glUniformMatrix4fv(comp->texture_shader.proj_uniform, 1, GL_FALSE,
|
|
output->matrix.d);
|
|
glUniform1i(comp->texture_shader.tex_uniform, 0);
|
|
|
|
/* TODO: Set cursor */
|
|
|
|
pixman_region32_init(&new_damage);
|
|
pixman_region32_init(&opaque);
|
|
|
|
wl_list_for_each(ws, &comp->surfaces, link)
|
|
{
|
|
pixman_region32_subtract(&ws->damage, &ws->damage, &opaque);
|
|
pixman_region32_union(&new_damage, &new_damage, &ws->damage);
|
|
pixman_region32_union(&opaque, &opaque, &ws->opaque);
|
|
}
|
|
|
|
pixman_region32_init(&total_damage);
|
|
pixman_region32_union(&total_damage, &new_damage, &output->prev_damage);
|
|
pixman_region32_intersect(&output->prev_damage, &new_damage, &output->region);
|
|
|
|
pixman_region32_fini(&opaque);
|
|
pixman_region32_fini(&new_damage);
|
|
|
|
ws = container_of(comp->surfaces.next, Wayland_Surface, link);
|
|
|
|
if (_e_mod_comp_wl_output_setup_scanout_surface(output, ws))
|
|
return;
|
|
|
|
/* TODO: Handle Fullscreen */
|
|
{
|
|
wl_list_for_each(ws, &comp->surfaces, link)
|
|
{
|
|
pixman_region32_copy(&ws->damage, &total_damage);
|
|
pixman_region32_subtract(&total_damage, &total_damage, &ws->opaque);
|
|
}
|
|
|
|
wl_list_for_each_reverse(ws, &comp->surfaces, link)
|
|
{
|
|
pixman_region32_init(&repaint);
|
|
pixman_region32_intersect(&repaint, &output->region, &ws->damage);
|
|
e_mod_comp_wl_surface_draw(ws, output, &repaint);
|
|
pixman_region32_subtract(&ws->damage, &ws->damage, &output->region);
|
|
pixman_region32_fini(&repaint);
|
|
}
|
|
}
|
|
|
|
/* TODO: Fade out */
|
|
|
|
pixman_region32_fini(&total_damage);
|
|
|
|
/* end weston output repaint */
|
|
|
|
output->repaint_needed = EINA_FALSE;
|
|
output->repaint_scheduled = EINA_TRUE;
|
|
output->present(output);
|
|
|
|
wl_list_for_each_safe(cb, cnext, &output->frame_callbacks, link)
|
|
{
|
|
wl_resource_post_event(&cb->resource, WL_CALLBACK_DONE, secs);
|
|
wl_resource_destroy(&cb->resource, 0);
|
|
}
|
|
|
|
/* TODO: Handle animations */
|
|
}
|
|
|
|
static void
|
|
_e_mod_comp_wl_output_schedule_repaint(void)
|
|
{
|
|
struct wl_event_loop *loop;
|
|
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
loop = wl_display_get_event_loop(_wl_disp);
|
|
_wl_output->repaint_needed = EINA_TRUE;
|
|
if (_wl_output->repaint_scheduled) return;
|
|
wl_event_loop_add_idle(loop, e_mod_comp_wl_output_idle_repaint, _wl_output);
|
|
_wl_output->repaint_scheduled = EINA_TRUE;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_e_mod_comp_wl_output_setup_scanout_surface(Wayland_Output *output, Wayland_Surface *ws)
|
|
{
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
if ((!output) || (!ws)) return EINA_FALSE;
|
|
|
|
if ((ws->visual != WAYLAND_RGB_VISUAL) ||
|
|
(output->prepare_scanout_surface(output, ws) != 0))
|
|
return EINA_FALSE;
|
|
|
|
output->pending_scanout_buffer = ws->buffer;
|
|
output->pending_scanout_buffer->busy_count++;
|
|
|
|
wl_list_insert(output->pending_scanout_buffer->resource.destroy_listener_list.prev,
|
|
&output->pending_scanout_buffer_destroy_listener.link);
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static int
|
|
_e_mod_comp_wl_output_prepare_render(Wayland_Output *output)
|
|
{
|
|
Wayland_Compositor *comp;
|
|
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
if (!output) return -1;
|
|
comp = e_mod_comp_wl_comp_get();
|
|
|
|
if (!eglMakeCurrent(comp->egl.display, output->egl_surface,
|
|
output->egl_surface, comp->egl.context))
|
|
{
|
|
EINA_LOG_ERR("Failed to make current\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_e_mod_comp_wl_output_present(Wayland_Output *output)
|
|
{
|
|
Wayland_Compositor *comp;
|
|
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
if (!output) return -1;
|
|
|
|
comp = e_mod_comp_wl_comp_get();
|
|
|
|
if (_e_mod_comp_wl_output_prepare_render(output)) return -1;
|
|
|
|
eglSwapBuffers(comp->egl.display, output->egl_surface);
|
|
|
|
wl_event_source_timer_update(output->finish_frame_timer, 10);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_e_mod_comp_wl_output_prepare_scanout_surface(Wayland_Output *output __UNUSED__, Wayland_Surface *surface __UNUSED__)
|
|
{
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
_e_mod_comp_wl_output_move(Wayland_Output *output, int32_t x, int32_t y)
|
|
{
|
|
int flip;
|
|
|
|
LOGFN(__FILE__, __LINE__, __FUNCTION__);
|
|
|
|
if (!output) return;
|
|
|
|
pixman_region32_init(&output->prev_damage);
|
|
pixman_region32_init_rect(&output->region, x, y,
|
|
output->width, output->height);
|
|
|
|
e_mod_comp_wl_matrix_init(&output->matrix);
|
|
e_mod_comp_wl_matrix_translate(&output->matrix,
|
|
-output->x - output->width / 2.0,
|
|
-output->y - output->height / 2.0, 0);
|
|
|
|
flip = (output->flags & WL_OUTPUT_FLIPPED) ? -1 : 1;
|
|
|
|
e_mod_comp_wl_matrix_scale(&output->matrix, 2.0 / output->width,
|
|
flip * 2.0 / output->height, 1);
|
|
e_mod_comp_wl_output_damage(output);
|
|
}
|