--- a/plugins/cube/cube.cpp +++ b/plugins/cube/cube.cpp @@ -336,7 +336,7 @@ // A vertical move translates complete Cube rows, including their // side and rear faces. Keep textures for every row so fast steps // and reversal preserve the visible position. - bool all_rows = cube->vertical_transition || cube->mouse_overview_active; + bool all_rows = cube->vertical_transition || cube->mouse_overview_active || cube->render_at_rest; int count = all_rows ? grid.height * grid.width : grid.width; for (int i = 0; i < count; i++) { @@ -392,11 +392,15 @@ wf::option_wrapper_t face_dim{"cube/dim"}; wf::option_wrapper_t resting_yaw{"cube/resting_yaw"}; wf::option_wrapper_t mouse_overview; + wf::option_wrapper_t render_at_rest; wf::option_wrapper_t rotate_layer_shell{"cube/rotate_layer_shell"}; wf::option_wrapper_t window_move_alpha{"vswitch/window_move_alpha"}; bool input_is_grabbed = false; wayfire_toplevel_view fixed_workspace_view = nullptr; bool cube_render_paused = false; + bool resting_renderer = false; + bool shutting_down = false; + bool return_to_rest = false; /* the Z camera distance so that (-1, 1) is mapped to the whole screen * for the given FOV */ @@ -473,6 +477,14 @@ return output->wset()->get_workspace_grid_size().width; } + float front_face_distance() + { + // Match the normal desktop pixel for pixel when the front face is + // full-size, using the actual projection rather than its approximation. + return render_at_rest ? animation.projection[1][1] * 0.5f + + (get_num_faces() == 2 ? 1e-3f : 0.0f) : Z_OFFSET_NEAR; + } + bool refresh_cube_geometry() { auto grid = output->wset()->get_workspace_grid_size(); @@ -505,8 +517,8 @@ { vertical_transition = false; row_translation.set(0, 0); - animation.cube_animation.offset_z.set(identity_z_offset + Z_OFFSET_NEAR, - identity_z_offset + Z_OFFSET_NEAR); + animation.cube_animation.offset_z.set(identity_z_offset + front_face_distance(), + identity_z_offset + front_face_distance()); rebuild_cube_render_node(); output->render->damage_whole(); } @@ -544,14 +556,18 @@ { // Promote ini values read with older metadata when hot-loading Cube. auto section = wf::get_core().config->get_section("cube"); - auto existing = section->get_option_or("mouse_overview"); - if (!std::dynamic_pointer_cast>(existing)) - { - auto option = std::make_shared>("mouse_overview", false); - if (existing) option->set_value_str(existing->get_value_str()); - section->register_new_option(option); + for (const auto& name : {"mouse_overview", "render_at_rest"}) + { + auto existing = section->get_option_or(name); + if (!std::dynamic_pointer_cast>(existing)) + { + auto option = std::make_shared>(name, false); + if (existing) option->set_value_str(existing->get_value_str()); + section->register_new_option(option); + } } mouse_overview.load_option("cube/mouse_overview"); + render_at_rest.load_option("cube/render_at_rest"); input_grab = std::make_unique("cube", output, nullptr, this, nullptr); input_grab->set_wants_raw_input(true); @@ -570,6 +586,8 @@ output->connect(&on_cube_render_pause); output->connect(&on_fixed_view_disappeared); output->connect(&on_view_geometry_changed); + output->connect(&on_rest_workspace_changed); + output->connect(&on_rest_workspace_set_changed); wf::get_core().connect(&on_fixed_view_set_output); wf::get_core().connect(&on_key_release); wf::get_core().connect(&on_modifier_change); @@ -577,6 +595,18 @@ { load_program(); }); + render_at_rest.set_callback([this] + { + if (output->is_plugin_active(grab_interface.name)) + { + rebuild_cube_render_node(); + output->render->damage_whole(); + } else + { + update_rest_renderer(); + } + }); + update_rest_renderer(); } void handle_pointer_button(const wlr_pointer_button_event& event) override @@ -664,7 +694,8 @@ end_vertical_transition(); end_mouse_overview(); - float offset_z = identity_z_offset + Z_OFFSET_NEAR; + return_to_rest = false; + float offset_z = identity_z_offset + front_face_distance(); animation.cube_animation.rotation.set(angle, angle); animation.cube_animation.zoom.set(zoom, zoom); @@ -688,6 +719,7 @@ } end_mouse_overview(); + return_to_rest = true; if (fixed_view && (fixed_view->get_output() != output)) { fixed_view = nullptr; @@ -708,8 +740,8 @@ animation.in_exit = false; animation.cube_animation.zoom.set(1.0, 1.0); - animation.cube_animation.offset_z.set(identity_z_offset + Z_OFFSET_NEAR, - identity_z_offset + Z_OFFSET_NEAR); + animation.cube_animation.offset_z.set(identity_z_offset + front_face_distance(), + identity_z_offset + front_face_distance()); animation.cube_animation.offset_y.set(0, 0); animation.cube_animation.ease_deformation.set(0, 0); @@ -836,6 +868,58 @@ } } + void remove_rest_renderer() + { + if (!resting_renderer) + { + return; + } + + output->render->rem_effect(&pre_hook); + if (render_node) wf::scene::remove_child(render_node); + render_node = nullptr; + resting_renderer = false; + output->render->set_require_depth_buffer(false); + clear_unused_fixed_top_node(); + output->render->damage_whole(); + } + + void update_rest_renderer() + { + if (output->is_plugin_active(grab_interface.name)) + { + return; + } + + remove_rest_renderer(); + if (!render_at_rest || shutting_down || cube_render_paused) + { + return; + } + + refresh_cube_geometry(); + resting_renderer = true; + return_to_rest = false; + animation.in_exit = false; + animation.cube_animation.rotation.set(0, 0); + animation.cube_animation.offset_y.set(0, 0); + animation.cube_animation.offset_z.set(identity_z_offset + front_face_distance(), + identity_z_offset + front_face_distance()); + animation.cube_animation.zoom.set(1, 1); + animation.cube_animation.ease_deformation.set(0, 0); + update_view_matrix(); + render_node = std::make_shared(this); + add_cube_render_node(); + output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); + output->render->set_require_depth_buffer(true); + output->render->damage_whole(); + } + + wf::signal::connection_t on_rest_workspace_changed = + [this] (auto) { if (resting_renderer) update_rest_renderer(); }; + wf::signal::connection_t on_rest_workspace_set_changed = + [this] (auto) { if (resting_renderer) update_rest_renderer(); }; + void pause_cube_render(bool pause) { if (pause) @@ -845,18 +929,26 @@ wf::scene::remove_child(render_node); render_node = nullptr; clear_unused_fixed_top_node(); - cube_render_paused = true; output->render->damage_whole(); } - - return; - } - - if (cube_render_paused && !render_node) + cube_render_paused = true; + + return; + } + + if (!cube_render_paused) + { + return; + } + + cube_render_paused = false; + if (resting_renderer || !output->is_plugin_active(grab_interface.name)) + { + update_rest_renderer(); + } else if (!render_node) { render_node = std::make_shared(this); add_cube_render_node(); - cube_render_paused = false; output->render->damage_whole(); } } @@ -994,14 +1086,15 @@ return false; } + remove_rest_renderer(); render_node = std::make_shared(this); add_cube_render_node(); output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); output->render->set_require_depth_buffer(true); reload_background(); - animation.cube_animation.offset_z.set(identity_z_offset + Z_OFFSET_NEAR, - identity_z_offset + Z_OFFSET_NEAR); + animation.cube_animation.offset_z.set(identity_z_offset + front_face_distance(), + identity_z_offset + front_face_distance()); return true; } @@ -1045,6 +1138,7 @@ vertical_transition = false; row_translation.set(0, 0); mouse_overview_active = false; + return_to_rest = false; overview_mix.set(0, 0); clear_fixed_workspace_overlay(); clear_unused_fixed_top_node(); @@ -1054,6 +1148,7 @@ /* We are finished with rotation, make sure the next time cube is used * it is properly reset */ animation.cube_animation.rotation.set(0, 0); + update_rest_renderer(); } /* Sets attributes target to such values that the cube effect isn't visible, @@ -1065,7 +1160,7 @@ { animation.cube_animation.zoom.restart_with_end(1.0); animation.cube_animation.offset_z.restart_with_end( - identity_z_offset + Z_OFFSET_NEAR); + identity_z_offset + front_face_distance()); animation.cube_animation.offset_y.restart_with_end(0); animation.cube_animation.ease_deformation.restart_with_end(0); } @@ -1080,6 +1175,7 @@ end_vertical_transition(); end_mouse_overview(); + return_to_rest = true; /* After the rotation is done, we want to exit cube and focus the target * workspace */ animation.in_exit = false; @@ -1088,8 +1184,8 @@ * and reset other attribs */ animation.cube_animation.zoom.set(1.0, 1.0); animation.cube_animation.offset_z.set( - identity_z_offset + Z_OFFSET_NEAR, - identity_z_offset + Z_OFFSET_NEAR); + identity_z_offset + front_face_distance(), + identity_z_offset + front_face_distance()); animation.cube_animation.offset_y.set(0, 0); animation.cube_animation.ease_deformation.set(0, 0); auto cws = output->wset()->get_current_workspace(); @@ -1135,6 +1231,7 @@ end_vertical_transition(); end_mouse_overview(); + return_to_rest = false; if (mouse_overview && output->wset()->get_workspace_grid_size().height > 1) { workspace_animation_anchor.reset(); @@ -1168,7 +1265,7 @@ animation.cube_animation.rotation.set(current_rotation, current_rotation); animation.cube_animation.offset_y.set(current_offset_y, current_offset_y); animation.cube_animation.offset_z.restart_with_end( - zoom_opt + identity_z_offset + Z_OFFSET_NEAR); + zoom_opt + identity_z_offset + front_face_distance()); animation.cube_animation.zoom.set(current_zoom, current_zoom); animation.cube_animation.ease_deformation.restart_with_end(1); @@ -1209,7 +1306,7 @@ } } - if (mouse_overview_active) + if ((mouse_overview_active || render_at_rest) && !vertical_transition) { // Keep all rows while zooming into the selected face. The anchor // prevents cancellation from applying the horizontal step twice. @@ -1237,8 +1334,8 @@ animation.cube_animation.rotation.set(current_rotation, current_rotation); animation.cube_animation.offset_y.set(0, 0); animation.cube_animation.offset_z.set( - identity_z_offset + Z_OFFSET_NEAR, - identity_z_offset + Z_OFFSET_NEAR); + identity_z_offset + front_face_distance(), + identity_z_offset + front_face_distance()); animation.cube_animation.zoom.set(1.0, 1.0); animation.cube_animation.ease_deformation.set(0, 0); @@ -1262,12 +1359,14 @@ glm::vec3(0., 1., 0.)); animation.view = zoom_translate * rotation * view; - if (mouse_overview_active) + if ((mouse_overview_active || render_at_rest) && !vertical_transition) { const float pitch = animation.cube_animation.offset_y; const float zoom = animation.cube_animation.zoom; const float rows = output->wset()->get_workspace_grid_size().height; - const float center = ((rows - 1) / 2 - float(overview_row)) / zoom; + const float row = mouse_overview_active ? float(overview_row) : + output->wset()->get_current_workspace().y; + const float center = ((rows - 1) / 2 - row) / zoom; auto pivot = glm::translate(glm::mat4(1.0), glm::vec3(0, center, 0)); auto unpivot = glm::translate(glm::mat4(1.0), glm::vec3(0, -center, 0)); // Orbit the complete stack about its middle. Move the camera back @@ -1285,17 +1384,12 @@ return wf::gles::render_target_gl_to_framebuffer(target) * scale; } - glm::mat4 calculate_vp_matrix(const wf::render_target_t& dest, float row_offset = 0.0f) + glm::mat4 calculate_vp_matrix(const wf::render_target_t& dest) { float zoom_factor = animation.cube_animation.zoom; auto scale_matrix = glm::scale(glm::mat4(1.0), glm::vec3(1. / zoom_factor, 1. / zoom_factor, 1. / zoom_factor)); - // Translate the complete projected row by its viewport height. Doing - // this before the output transform keeps side/rear content moving at - // the same speed as the front, including rotated/scaled outputs. - auto row_translation = glm::translate(glm::mat4(1.0), - glm::vec3(0, 2 * row_offset, 0)); if (mouse_overview_active) { // Fit every row while keeping the selected row centered. Scale @@ -1311,7 +1405,7 @@ // so adjoining faces meet along the same edge while rotating. return output_transform(dest) * frame * animation.projection * animation.view * scale_matrix; } - return output_transform(dest) * row_translation * animation.projection * animation.view * scale_matrix; + return output_transform(dest) * animation.projection * animation.view * scale_matrix; } /* Calculate the base model matrix for the i-th side of the cube */ @@ -1346,20 +1440,23 @@ auto cws = output->wset()->get_current_workspace(); const int faces = get_num_faces(); - const int rows = (vertical_transition || mouse_overview_active) ? (int)buffers.size() / faces : 1; + const int rows = (vertical_transition || mouse_overview_active || render_at_rest) ? + (int)buffers.size() / faces : 1; + const bool stack_rows = vertical_transition || mouse_overview_active || render_at_rest; for (int row = 0; row < rows; row++) { - // Mouse overview stacks faces in the shared model space. - // Keyboard row slides retain their projected viewport offsets. + // Every row shares one projection, including keyboard slides. + // Translate the joined faces in model space so rear edges meet. const float offset = mouse_overview_active ? row - float(overview_row) : - vertical_transition ? row - cws.y + float(row_translation) : 0.0f; - program.uniformMatrix4f("VP", calculate_vp_matrix(target, offset)); + vertical_transition ? row - cws.y + float(row_translation) : + stack_rows ? row - cws.y : 0.0f; + program.uniformMatrix4f("VP", calculate_vp_matrix(target)); for (int i = 0; i < faces; i++) { int index = row * faces + (cws.x + i) % faces; GL_CALL(glBindTexture(GL_TEXTURE_2D, wf::gles_texture_t::from_aux(buffers[index]).tex_id)); program.uniformMatrix4f("model", calculate_model_matrix(i, - mouse_overview_active ? offset : 0.0f)); + stack_rows ? offset : 0.0f)); if (tessellation_support) { @@ -1407,11 +1504,11 @@ program.attrib_pointer("position", 2, 0, vertexData); program.attrib_pointer("uvPosition", 2, 0, coordData); program.uniform1f("cube_brightness", - 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); + resting_renderer ? 1.0 : 1.0 - wf::clamp((double)face_dim, 0.0, 1.0)); if (tessellation_support) { - program.uniform1i("deform", use_deform); - program.uniform1i("light", use_light); + program.uniform1i("deform", resting_renderer ? 0 : int(use_deform)); + program.uniform1i("light", resting_renderer ? 0 : int(use_light)); program.uniform1f("ease", animation.cube_animation.ease_deformation); } @@ -1468,7 +1565,8 @@ if (running) { output->render->schedule_redraw(); - } else if (animation.in_exit) + } else if (animation.in_exit || + (render_at_rest && return_to_rest && !input_is_grabbed)) { deactivate(); } else { @@ -1642,11 +1740,13 @@ void fini() override { + shutting_down = true; clear_post_move_window_dim(); if (output->is_plugin_active(grab_interface.name)) { deactivate(); } + remove_rest_renderer(); wf::gles::run_in_context_if_gles([&] { --- a/metadata/cube.xml +++ b/metadata/cube.xml @@ -73,6 +73,11 @@ false +