--- a/plugins/cube/cube.cpp +++ b/plugins/cube/cube.cpp @@ -390,7 +390,8 @@ /* the Z camera distance so that (-1, 1) is mapped to the whole screen * for the given FOV */ - float identity_z_offset; + float identity_z_offset = 0.0f; + int geometry_faces = 0; OpenGL::program_t program; @@ -452,6 +453,31 @@ int get_num_faces() { return output->wset()->get_workspace_grid_size().width; + } + + bool refresh_cube_geometry() + { + int faces = get_num_faces(); + if (faces == geometry_faces) + { + return false; + } + + float old_offset = identity_z_offset; + animation.side_angle = 2 * M_PI / float(faces); + identity_z_offset = faces == 1 ? 0.0f : + 0.5 / std::tan(animation.side_angle / 2); + if (geometry_faces) + { + // Preserve the camera's distance from the cube when a new + // workspace count changes its radius. + float offset = animation.cube_animation.offset_z + + identity_z_offset - old_offset; + animation.cube_animation.offset_z.set(offset, offset); + } + + geometry_faces = faces; + return true; } wf::plugin_activation_data_t grab_interface{ @@ -888,8 +914,15 @@ /* Tries to initialize renderer, activate plugin, etc. */ bool activate() { + const bool geometry_changed = refresh_cube_geometry(); if (output->is_plugin_active(grab_interface.name)) { + if (geometry_changed) + { + rebuild_cube_render_node(); + output->render->damage_whole(); + } + return true; } @@ -903,15 +936,6 @@ output->render->add_effect(&pre_hook, wf::OUTPUT_EFFECT_PRE); output->render->set_require_depth_buffer(true); - auto wsize = output->wset()->get_workspace_grid_size(); - animation.side_angle = 2 * M_PI / float(wsize.width); - identity_z_offset = 0.5 / std::tan(animation.side_angle / 2); - if (wsize.width == 1) - { - // tan(M_PI) is 0, so identity_z_offset is invalid - identity_z_offset = 0.0f; - } - reload_background(); animation.cube_animation.offset_z.set(identity_z_offset + Z_OFFSET_NEAR, identity_z_offset + Z_OFFSET_NEAR); @@ -940,14 +964,17 @@ output->render->rem_effect(&pre_hook); output->render->set_require_depth_buffer(false); - /* Figure out how much we have rotated and switch workspace */ - int size = get_num_faces(); - int dvx = calculate_viewport_dx_from_rotation(); - - auto cws = workspace_animation_anchor.value_or( - output->wset()->get_current_workspace()); - int nvx = (cws.x + (dvx % size) + size) % size; - output->wset()->set_workspace({nvx, cws.y}); + // Vswitch already committed its destination before starting this + // animation. Cancelling it must retain that workspace. Interactive + // cube rotations still select the face nearest the current angle. + if (!workspace_animation_anchor) + { + int size = get_num_faces(); + int dvx = calculate_viewport_dx_from_rotation(); + auto cws = output->wset()->get_current_workspace(); + int nvx = (cws.x + (dvx % size) + size) % size; + output->wset()->set_workspace({nvx, cws.y}); + } fixed_workspace_view = nullptr; workspace_animation_anchor.reset(); @@ -1272,6 +1299,12 @@ bool animation_was_running = false; wf::effect_hook_t pre_hook = [=] () { + if (refresh_cube_geometry()) + { + rebuild_cube_render_node(); + output->render->damage_whole(); + } + update_view_matrix(); const bool running = animation.cube_animation.running(); if (render_node && (running || animation_was_running)) @@ -1289,7 +1322,7 @@ { deactivate(); } else { - if (fixed_workspace_view) + if (workspace_animation_anchor) { fixed_workspace_view = nullptr; workspace_animation_anchor.reset(); @@ -1404,7 +1437,7 @@ (view == fixed_workspace_view)) { fixed_workspace_view = nullptr; - workspace_animation_anchor.reset(); + // Losing the carried view does not cancel the workspace transition. clear_post_move_window_dim(); clear_fixed_workspace_overlay(false); rebuild_cube_render_node();