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 =