! Layer 15 — Sovereign IDE Application Shell ! Session state, workspace, main event loop, BOB context slot ! Maps to: bob-ide, bob-orchestrator MODULE sovereign_shell USE iso_c_binding USE sovereign_runtime, ONLY: runtime_init, runtime_teardown, runtime_panic, str_intern IMPLICIT NONE PRIVATE ! ── Workspace descriptor ───────────────────────────────────────────────── INTEGER, PARAMETER :: MAX_OPEN_FILES = 512 INTEGER, PARAMETER :: MAX_PANELS = 16 TYPE :: panel_t REAL :: x = 0.0, y = 0.0, w = 1.0, h = 1.0 INTEGER :: buffer_idx = 0 LOGICAL :: active = .FALSE. END TYPE panel_t TYPE :: workspace_t INTEGER(C_INT32_T) :: root_path_id = 0 INTEGER(C_INT32_T) :: open_files(MAX_OPEN_FILES) INTEGER :: open_count = 0 TYPE(panel_t) :: panels(MAX_PANELS) INTEGER :: panel_count = 1 INTEGER :: active_panel = 1 LOGICAL :: dirty = .FALSE. END TYPE workspace_t ! ── Session state (WORM-sealed) ────────────────────────────────────────── TYPE :: session_t TYPE(workspace_t) :: workspace INTEGER(C_INT64_T) :: frame_count = 0 LOGICAL :: bob_context_loaded = .FALSE. INTEGER(C_INT32_T) :: session_id = 0 LOGICAL :: running = .FALSE. END TYPE session_t TYPE(session_t), SAVE :: g_session PUBLIC :: shell_init PUBLIC :: shell_run PUBLIC :: shell_shutdown PUBLIC :: shell_open_file PUBLIC :: shell_close_file PUBLIC :: shell_split_panel PUBLIC :: shell_save_session PUBLIC :: shell_restore_session CONTAINS ! --------------------------------------------------------------------------- SUBROUTINE shell_init(workspace_root) CHARACTER(LEN=*), INTENT(IN) :: workspace_root CALL runtime_init() g_session%workspace%root_path_id = str_intern(workspace_root) g_session%workspace%panel_count = 1 g_session%workspace%active_panel = 1 g_session%workspace%panels(1)%active = .TRUE. g_session%running = .TRUE. g_session%frame_count = 0 WRITE(*,'(A,A)') '[shell] init workspace: ', TRIM(workspace_root) END SUBROUTINE shell_init ! --------------------------------------------------------------------------- ! Main event loop: poll → update → render → repeat SUBROUTINE shell_run() DO WHILE (g_session%running) g_session%frame_count = g_session%frame_count + 1 ! 1. Poll OS events (Layer 1 platform) ! TODO: CALL platform_poll_events() ! 2. Process input (Layer 2 GUI input dispatch) ! TODO: CALL gui_process_input() ! 3. Update all active layers ! TODO: CALL lsp_tick() — Layer 12 ! TODO: CALL dap_tick() — Layer 13 ! TODO: CALL ext_host_tick() — Layer 14 ! 4. Begin frame (Layer 2 GUI) ! TODO: CALL gui_begin_frame() ! 5. Render panels → text buffers → syntax highlighting ! TODO: CALL gui_render_panels() ! 6. End frame ! TODO: CALL gui_end_frame() ! 7. Auto-save session every 300 frames IF (MOD(g_session%frame_count, 300) == 0) CALL shell_save_session() ! Stub: exit after first frame for now g_session%running = .FALSE. END DO END SUBROUTINE shell_run ! --------------------------------------------------------------------------- SUBROUTINE shell_shutdown() CALL shell_save_session() ! Teardown in reverse order: 15 → 0 ! TODO: CALL ext_host_shutdown() — Layer 14 ! TODO: CALL dap_shutdown() — Layer 13 ! TODO: CALL lsp_shutdown() — Layer 12 ! TODO: CALL build_shutdown() — Layer 11 ! ... CALL runtime_teardown() WRITE(*,'(A,I0,A)') '[shell] shutdown after ', g_session%frame_count, ' frames' END SUBROUTINE shell_shutdown ! --------------------------------------------------------------------------- SUBROUTINE shell_open_file(path) CHARACTER(LEN=*), INTENT(IN) :: path INTEGER(C_INT32_T) :: fid IF (g_session%workspace%open_count >= MAX_OPEN_FILES) & CALL runtime_panic('shell_open_file: too many open files') fid = str_intern(path) g_session%workspace%open_count = g_session%workspace%open_count + 1 g_session%workspace%open_files(g_session%workspace%open_count) = fid g_session%workspace%dirty = .TRUE. WRITE(*,'(A,A)') '[shell] opened: ', TRIM(path) END SUBROUTINE shell_open_file ! --------------------------------------------------------------------------- SUBROUTINE shell_close_file(idx) INTEGER, INTENT(IN) :: idx INTEGER :: i IF (idx < 1 .OR. idx > g_session%workspace%open_count) RETURN DO i = idx, g_session%workspace%open_count - 1 g_session%workspace%open_files(i) = g_session%workspace%open_files(i+1) END DO g_session%workspace%open_count = g_session%workspace%open_count - 1 g_session%workspace%dirty = .TRUE. END SUBROUTINE shell_close_file ! --------------------------------------------------------------------------- SUBROUTINE shell_split_panel(direction) CHARACTER(LEN=*), INTENT(IN) :: direction INTEGER :: n n = g_session%workspace%panel_count IF (n >= MAX_PANELS) RETURN g_session%workspace%panel_count = n + 1 g_session%workspace%panels(n+1)%active = .TRUE. IF (direction == 'v') THEN g_session%workspace%panels(n)%w = g_session%workspace%panels(n)%w * 0.5 g_session%workspace%panels(n+1)%x = g_session%workspace%panels(n)%x + g_session%workspace%panels(n)%w g_session%workspace%panels(n+1)%w = g_session%workspace%panels(n)%w ELSE g_session%workspace%panels(n)%h = g_session%workspace%panels(n)%h * 0.5 g_session%workspace%panels(n+1)%y = g_session%workspace%panels(n)%y + g_session%workspace%panels(n)%h g_session%workspace%panels(n+1)%h = g_session%workspace%panels(n)%h END IF g_session%workspace%dirty = .TRUE. END SUBROUTINE shell_split_panel ! --------------------------------------------------------------------------- ! Session save: serialize workspace to WORM-sealed JSON SUBROUTINE shell_save_session() IF (.NOT. g_session%workspace%dirty) RETURN ! TODO: serialize workspace_t → JSON ! TODO: CALL bifrost_worm_append(session_json) — Layer 0 WORM g_session%workspace%dirty = .FALSE. WRITE(*,'(A,I0)') '[shell] session saved at frame ', g_session%frame_count END SUBROUTINE shell_save_session ! --------------------------------------------------------------------------- SUBROUTINE shell_restore_session(session_path) CHARACTER(LEN=*), INTENT(IN) :: session_path ! TODO: read WORM-sealed session file ! TODO: deserialize → workspace_t ! TODO: re-open all files (Layer 3 buffer restore) ! TODO: reconnect BOB context (Layer 14 ext_host_connect_bob) g_session%bob_context_loaded = .TRUE. WRITE(*,'(A,A)') '[shell] restored session: ', TRIM(session_path) END SUBROUTINE shell_restore_session END MODULE sovereign_shell