#include #include #include #include #include "state/wayland.h" #include "window.h" #include "window/wayland.h" #include "renderer.h" #include "shader.h" WaylandWindowImpl::WaylandWindowImpl(size_t width, size_t height) : m_frame_listener({0}), m_width(width), m_height(height), m_frame_cb_pending(false), m_redraw_requested(false) { m_wsurface = wl_compositor_create_surface(WaylandState::GetInstance()->m_compositor); if (m_wsurface == NULL) { fprintf(stderr, "ERROR: could not connect create a surface\n"); exit(1); } m_egl_window = wl_egl_window_create(m_wsurface, width, height); if (m_egl_window == NULL) { fprintf(stderr, "ERROR: could not create wl_egl_window\n"); exit(1); } m_egl_surface = Renderer::GetInstance()->CreateSurface(m_egl_window); if (m_egl_surface == NULL) { fprintf(stderr, "ERROR: could not create EGL window surface\n"); fprintf(stderr, "EGL error: 0x%x\n", eglGetError()); exit(1); } m_egl_context = Renderer::GetInstance()->CreateContext(); if (m_egl_context == NULL) { fprintf(stderr, "ERROR: could not create EGL context\n"); fprintf(stderr, "EGL error: 0x%x\n", eglGetError()); exit(1); } Renderer::GetInstance()->SurfaceSetContext(m_egl_surface, m_egl_context); const char *vs_src = "attribute vec2 pos;\n" "uniform float angle;\n" "varying vec2 vPos;\n" "void main() {\n" " float c = cos(angle);\n" " float s = sin(angle);\n" " mat2 rot = mat2(c, -s, s, c);\n" " gl_Position = vec4(rot * pos, 0.0, 1.0);\n" " vPos = rot * pos;\n" "}\n"; const char *fs_src = "precision mediump float;\n" "varying vec2 vPos;\n" "void main() {\n" " gl_FragColor = vec4(vPos, 0.2, 1.0);\n" "}\n"; m_program = create_program(vs_src, fs_src); glUseProgram(m_program); GLint pos_loc = glGetAttribLocation(m_program, "pos"); // GLint angle_loc = glGetUniformLocation(app_state->program, "angle"); float *vertices = new float[6]{0.0f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f}; glEnableVertexAttribArray(pos_loc); glVertexAttribPointer(pos_loc, 2, GL_FLOAT, GL_FALSE, 0, vertices); glViewport(0, 0, width, height); glClearColor(0.2f, 0.4f, 0.8f, 1.0f); m_xsurface = xdg_wm_base_get_xdg_surface( WaylandState::GetInstance()->m_wm_base, m_wsurface); if (m_xsurface == NULL) { fprintf(stderr, "ERROR: could not get an xdg surface\n"); exit(1); } m_toplevel = xdg_surface_get_toplevel(m_xsurface); if (m_toplevel == NULL) { fprintf(stderr, "ERROR: could not get an xdg toplevel\n"); exit(1); } m_xsurface_listener.configure = xsurface_handle_configure; xdg_surface_add_listener(m_xsurface, &m_xsurface_listener, this); m_toplevel_listener.configure = xtoplevel_handle_configure; m_toplevel_listener.configure_bounds = xtoplevel_handle_configure_bounds; m_toplevel_listener.close = xtoplevel_handle_close; m_toplevel_listener.wm_capabilities = xtoplevel_handle_wm_capabilities; xdg_toplevel_add_listener(m_toplevel, &m_toplevel_listener, this); m_running = true; wl_surface_commit(m_wsurface); } WaylandWindowImpl::~WaylandWindowImpl() { m_running = false; } void WaylandWindowImpl::OnFrame(IFrameListener fn) { m_on_frame = fn; } void WaylandWindowImpl::Init() { auto display = WaylandState::GetInstance()->m_display; assert(display && "wayland display is not initialized"); wl_display_dispatch_pending(display); } bool WaylandWindowImpl::Dispatch() { if (!m_running) return false; auto display = WaylandState::GetInstance()->m_display; printf("[DEBUG-WAYLAND]: checking if redraw needed, cb_pending = %d, " "redraw_req = %d\n", m_frame_cb_pending, m_redraw_requested); if (!m_frame_cb_pending && m_redraw_requested) { printf("[DEBUG-WAYLAND]: redraw requested\n"); render_frame(); auto frame_callback = wl_surface_frame(m_wsurface); m_frame_listener.done = handle_frame_callback; wl_callback_add_listener(frame_callback, &m_frame_listener, this); m_frame_cb_pending = true; m_redraw_requested = false; wl_surface_commit(m_wsurface); } // wl_display_dispatch_pending(display); return wl_display_dispatch(display) != -1; } size_t WaylandWindowImpl::GetWidth() const { return m_width; } size_t WaylandWindowImpl::GetHeight() const { return m_height; } void WaylandWindowImpl::render_frame() { GLint angle_loc = glGetUniformLocation(m_program, "angle"); static float angle = 0.0f; angle += 0.02f; glClear(GL_COLOR_BUFFER_BIT); glUniform1f(angle_loc, angle); glDrawArrays(GL_TRIANGLES, 0, 3); Renderer::GetInstance()->SwapBuffers(m_egl_surface); } void WaylandWindowImpl::handle_frame_callback(void *data, struct wl_callback *cb, uint32_t time) { UNUSED(time); auto window = reinterpret_cast(data); if (!window->m_running) return; wl_callback_destroy(cb); if (window->m_on_frame) window->m_on_frame(); window->m_frame_cb_pending = false; } void WaylandWindowImpl::xsurface_handle_configure(void *data, struct xdg_surface *surface, uint32_t serial) { printf("[DEBUG] xsurface_handle_configure event triggered\n"); auto window = reinterpret_cast(data); xdg_surface_ack_configure(surface, serial); window->m_redraw_requested = true; wl_surface_commit(window->m_wsurface); } void WaylandWindowImpl::xtoplevel_handle_configure( void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *states) { UNUSED(toplevel); auto window = reinterpret_cast(data); printf("[DEBUG] received toplevel configure event\n"); printf("[DEBUG] width = %d, height = %d\n", width, height); printf("[DEBUG] %zu states\n", states->size); wl_egl_window_resize(window->m_egl_window, width, height, 0, 0); glViewport(0, 0, width, height); xdg_surface_set_window_geometry(window->m_xsurface, 0, 0, width, height); window->m_width = width; window->m_height = height; window->m_redraw_requested = true; } void WaylandWindowImpl::xtoplevel_handle_close(void *data, struct xdg_toplevel *toplevel) { auto window = reinterpret_cast(data); UNUSED(toplevel); printf("[DEBUG] window close request received\n"); window->m_running = false; glDeleteProgram(window->m_program); Renderer::GetInstance()->DestroyWindow(window->m_egl_surface, window->m_egl_window); xdg_toplevel_destroy(window->m_toplevel); xdg_surface_destroy(window->m_xsurface); wl_surface_destroy(window->m_wsurface); Renderer::GetInstance()->DestroyContext(window->m_egl_context); } void WaylandWindowImpl::xtoplevel_handle_configure_bounds( void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height) { UNUSED(toplevel); printf("[DEBUG] window configure bounds, width = %d, height = %d\n", width, height); auto window = reinterpret_cast(data); window->m_redraw_requested = true; } void WaylandWindowImpl::xtoplevel_handle_wm_capabilities( void *data, struct xdg_toplevel *toplevel, struct wl_array *capabilities) { UNUSED(toplevel); UNUSED(capabilities); printf("[DEBUG] window wm_capabilities event received\n"); auto window = reinterpret_cast(data); window->m_redraw_requested = true; }