Mercurial
comparison infinite_canvas/main.c @ 282:6bd4a3990696 default tip
Add cross-platform canvas orchestration and typed composer
Enable native ARM dependencies, macOS Copilot orchestration, portable service supervision, CPU dictation, and a shared N-key speak-or-type composer.
Co-authored-by: Copilot <[email protected]>
Copilot-Session: f68442b1-fa8f-46a0-9689-81710613bbd4
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Thu, 20 Aug 2026 20:45:25 -0700 |
| parents | c57149ad216e |
| children |
comparison
equal
deleted
inserted
replaced
| 281:c57149ad216e | 282:6bd4a3990696 |
|---|---|
| 38 boolean pending_indicator_dragging; | 38 boolean pending_indicator_dragging; |
| 39 float pending_indicator_hover_amount; | 39 float pending_indicator_hover_amount; |
| 40 boolean dictation_overlay_visible; | 40 boolean dictation_overlay_visible; |
| 41 boolean dictation_listening; | 41 boolean dictation_listening; |
| 42 boolean dictation_requested_active; | 42 boolean dictation_requested_active; |
| 43 boolean dictation_typing; | |
| 44 boolean dictation_refocus_pending; | |
| 43 boolean dictation_send_pending; | 45 boolean dictation_send_pending; |
| 44 char dictation_status[128]; | 46 char dictation_status[128]; |
| 45 char dictation_partial[CANVAS_ENTITY_TEXT_CAPACITY]; | 47 char dictation_partial[CANVAS_ENTITY_TEXT_CAPACITY]; |
| 46 char dictation_transcript[CANVAS_ENTITY_TEXT_CAPACITY]; | 48 char dictation_transcript[CANVAS_ENTITY_TEXT_CAPACITY]; |
| 47 char pending_prompt[CANVAS_ENTITY_TEXT_CAPACITY]; | 49 char pending_prompt[CANVAS_ENTITY_TEXT_CAPACITY]; |
| 250 g_app->dictation_entity_id = p_entity->id; | 252 g_app->dictation_entity_id = p_entity->id; |
| 251 g_app->scene.selected_index = (int32)index; | 253 g_app->scene.selected_index = (int32)index; |
| 252 return p_entity; | 254 return p_entity; |
| 253 } | 255 } |
| 254 | 256 |
| 257 static void Canvas_App_Focus_Dictation_Entity(void) | |
| 258 { | |
| 259 Canvas_Entity *p_entity = Canvas_App_Ensure_Dictation_Entity(); | |
| 260 if (!p_entity) return; | |
| 261 for (size_t index = 0; | |
| 262 index < Dowa_Array_Length(g_app->scene.p_entities); | |
| 263 index++) { | |
| 264 if (g_app->scene.p_entities[index].id == p_entity->id) { | |
| 265 g_app->scene.selected_index = (int32)index; | |
| 266 break; | |
| 267 } | |
| 268 } | |
| 269 Canvas_Text_Area_Focus_End(p_entity, g_app->font); | |
| 270 } | |
| 271 | |
| 255 static void Canvas_App_Sync_Dictation_Entity(void) | 272 static void Canvas_App_Sync_Dictation_Entity(void) |
| 256 { | 273 { |
| 257 Canvas_Entity *p_entity = | 274 Canvas_Entity *p_entity = |
| 258 Canvas_App_Find_Entity(g_app->dictation_entity_id); | 275 Canvas_App_Find_Entity(g_app->dictation_entity_id); |
| 259 if (!p_entity) return; | 276 if (!p_entity) return; |
| 281 static void Canvas_App_Set_Dictation_Active(boolean active) | 298 static void Canvas_App_Set_Dictation_Active(boolean active) |
| 282 { | 299 { |
| 283 g_app->dictation_overlay_visible = TRUE; | 300 g_app->dictation_overlay_visible = TRUE; |
| 284 g_app->dictation_requested_active = active; | 301 g_app->dictation_requested_active = active; |
| 285 g_app->dictation_listening = FALSE; | 302 g_app->dictation_listening = FALSE; |
| 303 g_app->dictation_typing = FALSE; | |
| 286 g_app->dictation_send_pending = FALSE; | 304 g_app->dictation_send_pending = FALSE; |
| 287 if (active) { | 305 if (active) { |
| 288 Canvas_App_Ensure_Dictation_Entity(); | 306 Canvas_App_Ensure_Dictation_Entity(); |
| 289 Canvas_App_Sync_Dictation_Entity(); | 307 Canvas_App_Sync_Dictation_Entity(); |
| 290 } | 308 } |
| 296 Canvas_Web_Surface_Set_Dictation_Active( | 314 Canvas_Web_Surface_Set_Dictation_Active( |
| 297 &g_app->web_surface, | 315 &g_app->web_surface, |
| 298 active); | 316 active); |
| 299 } | 317 } |
| 300 | 318 |
| 319 static void Canvas_App_Start_Dictation_Typing(void) | |
| 320 { | |
| 321 if (g_app->dictation_requested_active || | |
| 322 g_app->dictation_listening) { | |
| 323 Canvas_Web_Surface_Set_Dictation_Active( | |
| 324 &g_app->web_surface, | |
| 325 FALSE); | |
| 326 } | |
| 327 g_app->dictation_requested_active = FALSE; | |
| 328 g_app->dictation_listening = FALSE; | |
| 329 g_app->dictation_typing = TRUE; | |
| 330 g_app->dictation_refocus_pending = FALSE; | |
| 331 g_app->dictation_send_pending = FALSE; | |
| 332 g_app->dictation_partial[0] = '\0'; | |
| 333 g_app->dictation_overlay_visible = TRUE; | |
| 334 Canvas_App_Sync_Dictation_Entity(); | |
| 335 Canvas_App_Focus_Dictation_Entity(); | |
| 336 snprintf( | |
| 337 g_app->dictation_status, | |
| 338 sizeof(g_app->dictation_status), | |
| 339 "Type a thought"); | |
| 340 | |
| 341 while (GetCharPressed() > 0) { | |
| 342 } | |
| 343 } | |
| 344 | |
| 345 static void Canvas_App_Sync_Typed_Dictation(void) | |
| 346 { | |
| 347 if (!g_app->dictation_typing) return; | |
| 348 Canvas_Entity *p_entity = | |
| 349 Canvas_App_Find_Entity(g_app->dictation_entity_id); | |
| 350 if (!p_entity) return; | |
| 351 snprintf( | |
| 352 g_app->dictation_transcript, | |
| 353 sizeof(g_app->dictation_transcript), | |
| 354 "%s", | |
| 355 p_entity->text); | |
| 356 } | |
| 357 | |
| 301 static boolean Canvas_App_Submit_Dictation(void) | 358 static boolean Canvas_App_Submit_Dictation(void) |
| 302 { | 359 { |
| 303 if (!g_app->dictation_transcript[0]) return FALSE; | 360 if (!g_app->dictation_transcript[0]) return FALSE; |
| 304 if (!Canvas_App_Submit_Prompt(g_app->dictation_transcript)) { | 361 if (!Canvas_App_Submit_Prompt(g_app->dictation_transcript)) { |
| 305 g_app->dictation_send_pending = FALSE; | 362 g_app->dictation_send_pending = FALSE; |
| 309 "Copilot is busy; press Enter to retry"); | 366 "Copilot is busy; press Enter to retry"); |
| 310 return FALSE; | 367 return FALSE; |
| 311 } | 368 } |
| 312 Canvas_Entity *p_entity = | 369 Canvas_Entity *p_entity = |
| 313 Canvas_App_Find_Entity(g_app->dictation_entity_id); | 370 Canvas_App_Find_Entity(g_app->dictation_entity_id); |
| 314 if (p_entity) p_entity->text_muted = FALSE; | 371 if (p_entity) { |
| 372 p_entity->text_muted = FALSE; | |
| 373 if (g_app->dictation_typing) p_entity->active = FALSE; | |
| 374 } | |
| 315 g_app->dictation_transcript[0] = '\0'; | 375 g_app->dictation_transcript[0] = '\0'; |
| 316 g_app->dictation_partial[0] = '\0'; | 376 g_app->dictation_partial[0] = '\0'; |
| 317 Canvas_App_Sync_Dictation_Entity(); | 377 Canvas_App_Sync_Dictation_Entity(); |
| 318 g_app->dictation_send_pending = FALSE; | 378 g_app->dictation_send_pending = FALSE; |
| 379 g_app->dictation_typing = FALSE; | |
| 380 g_app->dictation_refocus_pending = FALSE; | |
| 319 g_app->dictation_overlay_visible = | 381 g_app->dictation_overlay_visible = |
| 320 g_app->dictation_listening; | 382 g_app->dictation_listening; |
| 321 return TRUE; | 383 return TRUE; |
| 322 } | 384 } |
| 323 | 385 |
| 358 FALSE); | 420 FALSE); |
| 359 g_app->dictation_requested_active = FALSE; | 421 g_app->dictation_requested_active = FALSE; |
| 360 g_app->dictation_listening = FALSE; | 422 g_app->dictation_listening = FALSE; |
| 361 } | 423 } |
| 362 g_app->dictation_send_pending = FALSE; | 424 g_app->dictation_send_pending = FALSE; |
| 425 g_app->dictation_typing = FALSE; | |
| 363 g_app->dictation_partial[0] = '\0'; | 426 g_app->dictation_partial[0] = '\0'; |
| 364 g_app->dictation_transcript[0] = '\0'; | 427 g_app->dictation_transcript[0] = '\0'; |
| 428 Canvas_Entity *p_entity = | |
| 429 Canvas_App_Find_Entity(g_app->dictation_entity_id); | |
| 430 if (p_entity) p_entity->active = FALSE; | |
| 365 Canvas_App_Sync_Dictation_Entity(); | 431 Canvas_App_Sync_Dictation_Entity(); |
| 366 g_app->dictation_overlay_visible = FALSE; | 432 g_app->dictation_overlay_visible = FALSE; |
| 367 return; | 433 return; |
| 368 } | 434 } |
| 369 if (!IsKeyPressed(KEY_ENTER)) return; | 435 if (!IsKeyPressed(KEY_ENTER)) return; |
| 370 | 436 if (g_app->dictation_typing && |
| 371 if (g_app->dictation_partial[0]) { | 437 (IsKeyDown(KEY_LEFT_SHIFT) || |
| 438 IsKeyDown(KEY_RIGHT_SHIFT))) { | |
| 439 return; | |
| 440 } | |
| 441 | |
| 442 if (g_app->dictation_typing) { | |
| 443 Canvas_Entity *p_entity = | |
| 444 Canvas_App_Find_Entity(g_app->dictation_entity_id); | |
| 445 if (p_entity) p_entity->active = FALSE; | |
| 446 Canvas_App_Sync_Typed_Dictation(); | |
| 447 if (!Canvas_App_Submit_Dictation()) { | |
| 448 g_app->dictation_refocus_pending = TRUE; | |
| 449 } | |
| 450 } else if (g_app->dictation_partial[0]) { | |
| 372 g_app->dictation_send_pending = TRUE; | 451 g_app->dictation_send_pending = TRUE; |
| 373 snprintf( | 452 snprintf( |
| 374 g_app->dictation_status, | 453 g_app->dictation_status, |
| 375 sizeof(g_app->dictation_status), | 454 sizeof(g_app->dictation_status), |
| 376 "Finishing dictation..."); | 455 "Finishing dictation..."); |
| 618 boolean dictation_hotkey = | 697 boolean dictation_hotkey = |
| 619 !dev_ui_editing && | 698 !dev_ui_editing && |
| 620 !Canvas_Scene_Is_Text_Editing(&g_app->scene) && | 699 !Canvas_Scene_Is_Text_Editing(&g_app->scene) && |
| 621 !Canvas_Web_Chrome_Is_Editing(&g_app->scene) && | 700 !Canvas_Web_Chrome_Is_Editing(&g_app->scene) && |
| 622 IsKeyPressed(KEY_M); | 701 IsKeyPressed(KEY_M); |
| 702 boolean typing_hotkey = | |
| 703 !dev_ui_editing && | |
| 704 !Canvas_Scene_Is_Text_Editing(&g_app->scene) && | |
| 705 !Canvas_Web_Chrome_Is_Editing(&g_app->scene) && | |
| 706 IsKeyPressed(KEY_N); | |
| 623 if (dictation_hotkey) { | 707 if (dictation_hotkey) { |
| 624 Canvas_App_Set_Dictation_Active( | 708 Canvas_App_Set_Dictation_Active( |
| 625 g_app->dictation_requested_active ? FALSE : TRUE); | 709 g_app->dictation_requested_active ? FALSE : TRUE); |
| 710 } else if (typing_hotkey) { | |
| 711 Canvas_App_Start_Dictation_Typing(); | |
| 626 } | 712 } |
| 627 Canvas_App_Update_Dictation_Overlay_Input(); | 713 Canvas_App_Update_Dictation_Overlay_Input(); |
| 628 Canvas_Web_Chrome_Action chrome_action = CANVAS_WEB_CHROME_NONE; | 714 Canvas_Web_Chrome_Action chrome_action = CANVAS_WEB_CHROME_NONE; |
| 629 if (!block_pointer_input) { | 715 if (!block_pointer_input) { |
| 630 const char *p_current_url = NULL; | 716 const char *p_current_url = NULL; |
| 658 } | 744 } |
| 659 } | 745 } |
| 660 boolean web_input = FALSE; | 746 boolean web_input = FALSE; |
| 661 if (!block_pointer_input && | 747 if (!block_pointer_input && |
| 662 !dictation_hotkey && | 748 !dictation_hotkey && |
| 749 !typing_hotkey && | |
| 663 chrome_action == CANVAS_WEB_CHROME_NONE) { | 750 chrome_action == CANVAS_WEB_CHROME_NONE) { |
| 664 web_input = Canvas_Web_Surface_Update_Input( | 751 web_input = Canvas_Web_Surface_Update_Input( |
| 665 &g_app->web_surface, | 752 &g_app->web_surface, |
| 666 &g_app->camera, | 753 &g_app->camera, |
| 667 &g_app->scene); | 754 &g_app->scene); |
| 673 boolean entity_input = Canvas_Scene_Update_Interaction( | 760 boolean entity_input = Canvas_Scene_Update_Interaction( |
| 674 &g_app->scene, | 761 &g_app->scene, |
| 675 &g_app->camera, | 762 &g_app->camera, |
| 676 g_app->font, | 763 g_app->font, |
| 677 block_pointer_input); | 764 block_pointer_input); |
| 765 if (g_app->dictation_refocus_pending) { | |
| 766 Canvas_App_Focus_Dictation_Entity(); | |
| 767 g_app->dictation_refocus_pending = FALSE; | |
| 768 } | |
| 769 Canvas_App_Sync_Typed_Dictation(); | |
| 678 boolean keyboard_focus = | 770 boolean keyboard_focus = |
| 679 Canvas_Scene_Is_Text_Editing(&g_app->scene) || | 771 Canvas_Scene_Is_Text_Editing(&g_app->scene) || |
| 680 Canvas_Web_Chrome_Is_Editing(&g_app->scene) || | 772 Canvas_Web_Chrome_Is_Editing(&g_app->scene) || |
| 681 dev_ui_editing || | 773 dev_ui_editing || |
| 682 Canvas_Web_Surface_Is_Focused(&g_app->web_surface); | 774 Canvas_Web_Surface_Is_Focused(&g_app->web_surface); |
| 715 g_app->dictation_partial); | 807 g_app->dictation_partial); |
| 716 g_app->dictation_partial[0] = '\0'; | 808 g_app->dictation_partial[0] = '\0'; |
| 717 } | 809 } |
| 718 Canvas_App_Submit_Dictation(); | 810 Canvas_App_Submit_Dictation(); |
| 719 } else if (!g_app->dictation_transcript[0] && | 811 } else if (!g_app->dictation_transcript[0] && |
| 720 !g_app->dictation_partial[0]) { | 812 !g_app->dictation_partial[0] && |
| 813 !g_app->dictation_typing) { | |
| 721 g_app->dictation_overlay_visible = FALSE; | 814 g_app->dictation_overlay_visible = FALSE; |
| 722 } | 815 } |
| 723 } else if (strcmp(dictation_text, "Listening") == 0 || | 816 } else if (strcmp(dictation_text, "Listening") == 0 || |
| 724 strcmp(dictation_text, "Speech detected") == 0) { | 817 strcmp(dictation_text, "Speech detected") == 0) { |
| 725 g_app->dictation_listening = | 818 g_app->dictation_listening = |