view infinite_canvas/web_surface_native.cc @ 279:b3b547563ec7

Add Google connector service and agent wiki Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code. Co-authored-by: Copilot <[email protected]> Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
author MrJuneJune <me@mrjunejune.com>
date Mon, 17 Aug 2026 22:22:36 -0700
parents 8d560f50ed4c
children 49e9e591c9bb
line wrap: on
line source

extern "C" {
#include "infinite_canvas/web_surface.h"
}

#include "include/cef_app.h"
#include "include/cef_browser.h"
#include "include/cef_client.h"
#include "include/cef_display_handler.h"
#include "include/cef_render_handler.h"
#include "include/capi/cef_app_capi.h"

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <new>

#if defined(OS_LINUX)
#include <dlfcn.h>
#include <limits.h>
#endif

namespace {

constexpr int32 kResolutionStep = 64;
constexpr int32 kMinTextureWidth = 160;
constexpr int32 kMinTextureHeight = 120;
constexpr int32 kMaxTextureWidth = 1280;
constexpr int32 kMaxTextureHeight = 800;
int g_argc = 0;
char **g_argv = nullptr;
char *g_cef_argv[128] = {};
#if defined(OS_LINUX)
char g_resources_path[PATH_MAX + 32] = {};
char g_locales_path[PATH_MAX + 32] = {};
char g_resources_switch[PATH_MAX + 64] = {};
char g_locales_switch[PATH_MAX + 64] = {};
#endif

void FindResourcePaths();

void ApplyColorScheme(CefRefPtr<CefBrowser> browser, boolean dark_mode) {
  if (!browser) return;
  CefRefPtr<CefDictionaryValue> params = CefDictionaryValue::Create();
  CefRefPtr<CefListValue> features = CefListValue::Create();
  CefRefPtr<CefDictionaryValue> feature = CefDictionaryValue::Create();
  feature->SetString("name", "prefers-color-scheme");
  feature->SetString("value", dark_mode ? "dark" : "light");
  features->SetDictionary(0, feature);
  params->SetList("features", features);
  browser->GetHost()->ExecuteDevToolsMethod(
      0,
      "Emulation.setEmulatedMedia",
      params);

  CefRefPtr<CefFrame> frame = browser->GetMainFrame();
  const char *script = dark_mode
      ? "document.documentElement.style.colorScheme='dark'"
      : "document.documentElement.style.colorScheme='light'";
  frame->ExecuteJavaScript(script, frame->GetURL(), 0);
}

const Canvas_Entity *FindSurfaceEntity(
    const Canvas_Scene *scene,
    uint32 entity_id) {
  for (size_t index = 0; index < Dowa_Array_Length(scene->p_entities); ++index) {
    const Canvas_Entity *entity = &scene->p_entities[index];
    if ((entity->type == CANVAS_ENTITY_WEB_CONTENT ||
         entity->type == CANVAS_ENTITY_IMAGE) &&
        entity->id == entity_id) {
      return entity;
    }
  }
  return nullptr;
}

Rectangle ContentBounds(
    const Canvas_Camera *camera,
    const Canvas_Scene *scene,
    const Canvas_Entity *entity) {
  (void)scene;
  float frame_inset = CANVAS_WEB_FRAME_INSET / camera->zoom;
  float amount = std::clamp(entity->web_chrome_amount, 0.0f, 1.0f);
  float eased = amount * amount * (3.0f - 2.0f * amount);
  float toolbar_height = CANVAS_WEB_TOOLBAR_HEIGHT * eased / camera->zoom;
  float resize_handle = CANVAS_WEB_RESIZE_HANDLE / camera->zoom;
  Vector2 position = Canvas_Camera_World_To_Screen(
      camera,
      Vector2{
          entity->position.x + frame_inset,
          entity->position.y + frame_inset + toolbar_height});
  return Rectangle{
      position.x,
      position.y,
      (entity->size.x - frame_inset - resize_handle) * camera->zoom,
      (entity->size.y - frame_inset - resize_handle - toolbar_height) *
          camera->zoom,
  };
}

boolean IsVisible(const Canvas_Camera *camera, Rectangle bounds) {
  return camera->zoom >= 0.30f &&
      bounds.x + bounds.width > 0.0f &&
      bounds.y + bounds.height > 0.0f &&
      bounds.x < static_cast<float>(camera->viewport_width) &&
      bounds.y < static_cast<float>(camera->viewport_height);
}

float EntityOpacity(const Canvas_Entity *entity) {
  float amount = std::clamp(entity->visibility_amount, 0.0f, 1.0f);
  return amount * amount * (3.0f - 2.0f * amount);
}

int32 FrameRateForView(
    const Canvas_Web_Surface *surface,
    const Canvas_Web_View *view,
    Rectangle bounds) {
  if (surface->focused_entity_id == view->entity_id) return 30;
  float area = std::max(bounds.width, 0.0f) * std::max(bounds.height, 0.0f);
  return area <= 220000.0f ? 12 : 20;
}

int32 BucketDimension(float value, int32 minimum, int32 maximum) {
  int32 rounded = static_cast<int32>(
      std::ceil(std::max(value, 1.0f) / kResolutionStep)) * kResolutionStep;
  return std::clamp(rounded, minimum, maximum);
}

class CanvasCefApp : public CefApp,
                     public CefBrowserProcessHandler {
 public:
  CanvasCefApp() = default;

  CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() override {
    return this;
  }

  void OnBeforeCommandLineProcessing(
      const CefString &process_type,
      CefRefPtr<CefCommandLine> command_line) override {
    (void)process_type;
    command_line->AppendSwitch("no-sandbox");
    command_line->AppendSwitch("disable-gpu");
    command_line->AppendSwitch("disable-gpu-compositing");
    command_line->AppendSwitch("allow-file-access-from-files");
    command_line->AppendSwitch("allow-universal-access-from-files");
    command_line->AppendSwitchWithValue("log-severity", "disable");
#if defined(OS_LINUX)
    if (g_resources_path[0]) {
      command_line->AppendSwitchWithValue(
          "resources-dir-path",
          g_resources_path);
      command_line->AppendSwitchWithValue(
          "locales-dir-path",
          g_locales_path);
    }
#endif
  }

 private:
  IMPLEMENT_REFCOUNTING(CanvasCefApp);
  DISALLOW_COPY_AND_ASSIGN(CanvasCefApp);
};

class CanvasCefClient : public CefClient,
                        public CefRenderHandler,
                        public CefDisplayHandler,
                        public CefLifeSpanHandler {
 public:
  explicit CanvasCefClient(Canvas_Web_View *view) : view_(view) {}

  CefRefPtr<CefRenderHandler> GetRenderHandler() override { return this; }
  CefRefPtr<CefDisplayHandler> GetDisplayHandler() override { return this; }
  CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; }

  void OnAddressChange(
      CefRefPtr<CefBrowser> browser,
      CefRefPtr<CefFrame> frame,
      const CefString &url) override {
    if (!frame->IsMain()) return;
    std::snprintf(
        view_->url,
        sizeof(view_->url),
        "%s",
        url.ToString().c_str());
    view_->can_go_back = browser->CanGoBack() ? TRUE : FALSE;
    view_->can_go_forward = browser->CanGoForward() ? TRUE : FALSE;
  }

  void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect) override {
    (void)browser;
    rect = CefRect(0, 0, view_->texture_width, view_->texture_height);
  }

  void OnPaint(
      CefRefPtr<CefBrowser> browser,
      PaintElementType type,
      const RectList &dirty_rects,
      const void *buffer,
      int width,
      int height) override {
    (void)browser;
    (void)dirty_rects;
    if (type != PET_VIEW || !view_->p_pixels ||
        width != view_->texture_width ||
        height != view_->texture_height) {
      return;
    }

    const uint8 *source = static_cast<const uint8 *>(buffer);
    uint8 *target = view_->p_pixels;
    const size_t pixel_count = static_cast<size_t>(width) * height;
    for (size_t index = 0; index < pixel_count; ++index) {
      target[index * 4 + 0] = source[index * 4 + 2];
      target[index * 4 + 1] = source[index * 4 + 1];
      target[index * 4 + 2] = source[index * 4 + 0];
      target[index * 4 + 3] = source[index * 4 + 3];
    }
    view_->pixels_dirty = TRUE;
  }

  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
    browser_ = browser;
    browser_->GetHost()->WasHidden(!view_->active);
  }

  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
    (void)browser;
    browser_ = nullptr;
  }

  CefRefPtr<CefBrowser> browser() const { return browser_; }

 private:
  Canvas_Web_View *view_;
  CefRefPtr<CefBrowser> browser_;

  IMPLEMENT_REFCOUNTING(CanvasCefClient);
  DISALLOW_COPY_AND_ASSIGN(CanvasCefClient);
};

struct NativeState {
  CefRefPtr<CanvasCefClient> clients[CANVAS_MAX_WEB_VIEWS];
  boolean cef_initialized;
};

CefMainArgs MainArgs(int argc, char **argv) {
#if defined(OS_WIN)
  (void)argc;
  (void)argv;
  return CefMainArgs(GetModuleHandle(nullptr));
#else
  return CefMainArgs(argc, argv);
#endif
}

#if defined(OS_LINUX)
void FindResourcePaths() {
  if (g_resources_path[0]) return;
  Dl_info info = {};
  if (!dladdr(reinterpret_cast<void *>(&cef_initialize), &info) ||
      !info.dli_fname) {
    return;
  }

  char root[PATH_MAX] = {};
  if (!realpath(info.dli_fname, root)) return;
  char *release = std::strstr(root, "/Release/libcef.so");
  if (!release) return;
  *release = '\0';

  std::snprintf(
      g_resources_path,
      sizeof(g_resources_path),
      "%s/Resources",
      root);
  std::snprintf(
      g_locales_path,
      sizeof(g_locales_path),
      "%s/Resources/locales",
      root);
}

void ConfigureLinuxResourcePaths(CefSettings &settings) {
  FindResourcePaths();
  CefString(&settings.resources_dir_path) = g_resources_path;
  CefString(&settings.locales_dir_path) = g_locales_path;
}
#else
void FindResourcePaths() {}
#endif

int32 ViewIndex(
    const Canvas_Web_Surface *surface,
    const Canvas_Web_View *view) {
  return static_cast<int32>(view - surface->views);
}

Canvas_Web_View *FindView(Canvas_Web_Surface *surface, uint32 entity_id) {
  for (int32 index = 0; index < CANVAS_MAX_WEB_VIEWS; ++index) {
    if (surface->views[index].entity_id == entity_id) {
      return &surface->views[index];
    }
  }
  return nullptr;
}

const Canvas_Web_View *FindView(
    const Canvas_Web_Surface *surface,
    uint32 entity_id) {
  for (int32 index = 0; index < CANVAS_MAX_WEB_VIEWS; ++index) {
    if (surface->views[index].entity_id == entity_id) {
      return &surface->views[index];
    }
  }
  return nullptr;
}

Canvas_Web_View *FindInactiveView(Canvas_Web_Surface *surface) {
  for (int32 index = 0; index < CANVAS_MAX_WEB_VIEWS; ++index) {
    if (!surface->views[index].active) return &surface->views[index];
  }
  return nullptr;
}

boolean SceneHasVisibleEntity(
    const Canvas_Scene *scene,
    const Canvas_Camera *camera,
    uint32 entity_id) {
  const Canvas_Entity *entity = FindSurfaceEntity(scene, entity_id);
  return entity && IsVisible(camera, ContentBounds(camera, scene, entity));
}

boolean EnsureViewResolution(
    Canvas_Web_Surface *surface,
    Canvas_Web_View *view,
    Rectangle bounds) {
  int32 width = BucketDimension(
      bounds.width,
      kMinTextureWidth,
      kMaxTextureWidth);
  int32 height = BucketDimension(
      bounds.height,
      kMinTextureHeight,
      kMaxTextureHeight);
  if (view->p_pixels &&
      view->texture_width == width &&
      view->texture_height == height) {
    return TRUE;
  }

  size_t pixel_capacity =
      static_cast<size_t>(width) * static_cast<size_t>(height) * 4;
  void *resized_pixels = std::realloc(view->p_pixels, pixel_capacity);
  if (!resized_pixels) return FALSE;
  view->p_pixels = static_cast<uint8 *>(resized_pixels);
  view->pixel_capacity = pixel_capacity;
  view->texture_width = width;
  view->texture_height = height;
  std::memset(view->p_pixels, 255, pixel_capacity);
  view->pixels_dirty = FALSE;

  if (view->texture_ready) {
    UnloadTexture(view->texture);
    view->texture = {};
    view->texture_ready = FALSE;
  }

  NativeState *state = static_cast<NativeState *>(surface->p_native);
  int32 index = ViewIndex(surface, view);
  CefRefPtr<CanvasCefClient> client = state->clients[index];
  if (client && client->browser()) {
    client->browser()->GetHost()->WasResized();
  }
  return TRUE;
}

boolean CreateBrowser(
    Canvas_Web_Surface *surface,
    Canvas_Web_View *view,
    const char *url,
    Rectangle bounds) {
  NativeState *state = static_cast<NativeState *>(surface->p_native);
  int32 index = ViewIndex(surface, view);
  if (!EnsureViewResolution(surface, view, bounds)) return FALSE;

  state->clients[index] = new CanvasCefClient(view);
  CefWindowInfo window_info;
  window_info.SetAsWindowless(kNullWindowHandle);
  CefBrowserSettings browser_settings;
  browser_settings.windowless_frame_rate = 30;
  browser_settings.background_color = surface->dark_mode
      ? CefColorSetARGB(255, 15, 15, 17)
      : CefColorSetARGB(255, 255, 255, 255);
  const char *initial_url =
      view->entity_type == CANVAS_ENTITY_IMAGE ? "about:blank" : url;
  if (!CefBrowserHost::CreateBrowser(
          window_info,
          state->clients[index],
          initial_url,
          browser_settings,
          nullptr,
          nullptr)) {
    state->clients[index] = nullptr;
    return FALSE;
  }
  view->browser_created = TRUE;
  return TRUE;
}

void PercentEncode(
    const char *source,
    char *target,
    size_t target_size) {
  static const char hex[] = "0123456789ABCDEF";
  size_t output = 0;
  for (size_t index = 0; source[index] && output + 4 < target_size; ++index) {
    uint8 value = static_cast<uint8>(source[index]);
    boolean unreserved =
        (value >= 'a' && value <= 'z') ||
        (value >= 'A' && value <= 'Z') ||
        (value >= '0' && value <= '9') ||
        value == '-' || value == '_' || value == '.' || value == '~';
    if (unreserved) {
      target[output++] = static_cast<char>(value);
    } else {
      target[output++] = '%';
      target[output++] = hex[value >> 4];
      target[output++] = hex[value & 15];
    }
  }
  target[output] = '\0';
}

void LoadImageSource(
    CefRefPtr<CefBrowser> browser,
    const char *source) {
  char resolved[512] = {};
  boolean local_file =
      !std::strstr(source, "://") && std::strncmp(source, "data:", 5) != 0;
  if (!local_file) {
    std::snprintf(resolved, sizeof(resolved), "%s", source);
  } else {
    std::error_code error;
    std::filesystem::path absolute = std::filesystem::absolute(source, error);
    if (error) {
      std::snprintf(resolved, sizeof(resolved), "%s", source);
    } else {
      std::snprintf(
          resolved,
          sizeof(resolved),
          "file://%s",
          absolute.generic_string().c_str());
    }
  }
  std::error_code viewer_error;
  std::filesystem::path viewer_path = std::filesystem::absolute(
      "infinite_canvas/assets/image-view.html",
      viewer_error);
  if (viewer_error) {
    browser->GetMainFrame()->LoadURL(resolved);
    return;
  }
  char encoded_source[2048] = {};
  PercentEncode(resolved, encoded_source, sizeof(encoded_source));
  char viewer_url[3072] = {};
  std::snprintf(
      viewer_url,
      sizeof(viewer_url),
      "file://%s?src=%s",
      viewer_path.generic_string().c_str(),
      encoded_source);
  browser->GetMainFrame()->LoadURL(viewer_url);
}

void BindView(
    Canvas_Web_Surface *surface,
    Canvas_Web_View *view,
    const Canvas_Entity *entity,
    Rectangle bounds) {
  NativeState *state = static_cast<NativeState *>(surface->p_native);
  int32 index = ViewIndex(surface, view);
  boolean type_changed = view->entity_type != entity->type;
  view->active = TRUE;
  view->entity_id = entity->id;
  view->entity_type = entity->type;
  if (!view->browser_created) {
    if (!CreateBrowser(surface, view, entity->text, bounds)) {
      view->active = FALSE;
      view->entity_id = 0;
      return;
    }
    if (entity->type == CANVAS_ENTITY_WEB_CONTENT) {
      std::snprintf(
          view->source_url,
          sizeof(view->source_url),
          "%s",
          entity->text);
      std::snprintf(view->url, sizeof(view->url), "%s", entity->text);
    }
    return;
  }
  EnsureViewResolution(surface, view, bounds);

  if (type_changed || std::strcmp(view->source_url, entity->text) != 0) {
    CefRefPtr<CanvasCefClient> client = state->clients[index];
    if (client && client->browser()) {
      if (entity->type == CANVAS_ENTITY_IMAGE) {
        LoadImageSource(client->browser(), entity->text);
      } else {
        client->browser()->GetMainFrame()->LoadURL(entity->text);
      }
      std::snprintf(
          view->source_url,
          sizeof(view->source_url),
          "%s",
          entity->text);
      std::snprintf(view->url, sizeof(view->url), "%s", entity->text);
    }
  }
}

void ReconcileViews(
    Canvas_Web_Surface *surface,
    const Canvas_Camera *camera,
    const Canvas_Scene *scene) {
  for (int32 index = 0; index < CANVAS_MAX_WEB_VIEWS; ++index) {
    Canvas_Web_View *view = &surface->views[index];
    view->active = view->entity_id != 0 &&
        SceneHasVisibleEntity(scene, camera, view->entity_id);
  }

  for (size_t index = 0; index < Dowa_Array_Length(scene->p_entities); ++index) {
    const Canvas_Entity *entity = &scene->p_entities[index];
    if ((entity->type != CANVAS_ENTITY_WEB_CONTENT &&
         entity->type != CANVAS_ENTITY_IMAGE) ||
        !IsVisible(camera, ContentBounds(camera, scene, entity))) {
      continue;
    }
    Rectangle bounds = ContentBounds(camera, scene, entity);
    Canvas_Web_View *view = FindView(surface, entity->id);
    if (view) {
      BindView(surface, view, entity, bounds);
      continue;
    }
    view = FindInactiveView(surface);
    if (!view) break;
    BindView(surface, view, entity, bounds);
  }

  if (surface->focused_entity_id != 0) {
    Canvas_Web_View *focused = FindView(surface, surface->focused_entity_id);
    if (!focused || !focused->active) surface->focused_entity_id = 0;
  }

  NativeState *state = static_cast<NativeState *>(surface->p_native);
  for (int32 index = 0; index < CANVAS_MAX_WEB_VIEWS; ++index) {
    CefRefPtr<CanvasCefClient> client = state->clients[index];
    if (client && client->browser()) {
      Canvas_Web_View *view = &surface->views[index];
      CefRefPtr<CefBrowserHost> host = client->browser()->GetHost();
      host->WasHidden(!view->active);
      if (view->active) {
        const Canvas_Entity *entity = FindSurfaceEntity(scene, view->entity_id);
        if (entity) {
          host->SetWindowlessFrameRate(
              FrameRateForView(
                  surface,
                  view,
                  ContentBounds(camera, scene, entity)));
        }
      }
    }
  }
}

}  // namespace

extern "C" int32 Canvas_Web_Surface_Execute_Subprocess(
    int argc,
    char **p_argv) {
  g_argc = argc;
  FindResourcePaths();
  int32 cef_argc = 0;
  for (; cef_argc < argc && cef_argc < 120; ++cef_argc) {
    g_cef_argv[cef_argc] = p_argv[cef_argc];
  }
#if defined(OS_LINUX)
  if (g_resources_path[0]) {
    std::snprintf(
        g_resources_switch,
        sizeof(g_resources_switch),
        "--resources-dir-path=%s",
        g_resources_path);
    std::snprintf(
        g_locales_switch,
        sizeof(g_locales_switch),
        "--locales-dir-path=%s",
        g_locales_path);
    g_cef_argv[cef_argc++] = g_resources_switch;
    g_cef_argv[cef_argc++] = g_locales_switch;
  }
#endif
  g_cef_argv[cef_argc++] = const_cast<char *>("--no-sandbox");
  g_cef_argv[cef_argc++] = const_cast<char *>("--disable-gpu");
  g_cef_argv[cef_argc++] = const_cast<char *>("--disable-gpu-compositing");
  g_argc = cef_argc;
  g_argv = g_cef_argv;
  CefMainArgs args = MainArgs(g_argc, g_argv);
  CefRefPtr<CanvasCefApp> app = new CanvasCefApp();
  return CefExecuteProcess(args, app, nullptr);
}

extern "C" boolean Canvas_Web_Surface_Init(
    Canvas_Web_Surface *p_surface,
    Dowa_Arena *p_arena,
    boolean dark_mode) {
  std::memset(p_surface, 0, sizeof(*p_surface));
  p_surface->p_arena = p_arena;
  p_surface->dark_mode = dark_mode;
  NativeState *state = static_cast<NativeState *>(
      Dowa_Arena_Allocate(p_arena, sizeof(NativeState)));
  if (!state) return FALSE;

  new (state) NativeState();
  p_surface->p_native = state;

  CefSettings settings;
  settings.windowless_rendering_enabled = true;
  settings.no_sandbox = true;
  settings.multi_threaded_message_loop = false;
  settings.external_message_pump = false;
  settings.log_severity = LOGSEVERITY_DISABLE;
  const char *cache_path = std::getenv("INFINITE_CANVAS_CEF_CACHE");
  if (cache_path) {
    CefString(&settings.root_cache_path) = cache_path;
    CefString(&settings.cache_path) = cache_path;
  }
#if defined(OS_LINUX)
  ConfigureLinuxResourcePaths(settings);
#endif

  CefMainArgs args = MainArgs(g_argc, g_argv);
  CefRefPtr<CanvasCefApp> app = new CanvasCefApp();
  if (!CefInitialize(args, settings, app, nullptr)) {
    state->~NativeState();
    p_surface->p_native = nullptr;
    return FALSE;
  }
  state->cef_initialized = TRUE;
  p_surface->initialized = TRUE;
  return TRUE;
}

extern "C" void Canvas_Web_Surface_Set_Dark_Mode(
    Canvas_Web_Surface *p_surface,
    boolean dark_mode) {
  if (!p_surface->initialized || p_surface->dark_mode == dark_mode) return;
  p_surface->dark_mode = dark_mode;
}

extern "C" boolean Canvas_Web_Surface_Update_Input(
    Canvas_Web_Surface *p_surface,
    const Canvas_Camera *p_camera,
    const Canvas_Scene *p_scene) {
  if (!p_surface->initialized || !p_surface->p_native) return FALSE;
  NativeState *state = static_cast<NativeState *>(p_surface->p_native);
  Vector2 mouse = GetMousePosition();
  Canvas_Web_View *pointer_view = nullptr;
  const Canvas_Entity *pointer_entity = nullptr;
  Rectangle pointer_bounds = {};

  Vector2 world_mouse = Canvas_Camera_Screen_To_World(p_camera, mouse);
  int32 top_index = Canvas_Scene_Topmost_Visual(
      p_scene,
      world_mouse,
      p_camera->zoom);
  if (top_index >= 0) {
    const Canvas_Entity *entity = &p_scene->p_entities[top_index];
    if (entity->type == CANVAS_ENTITY_WEB_CONTENT &&
        !entity->removing &&
        EntityOpacity(entity) >= 0.99f) {
      Canvas_Web_View *view = FindView(p_surface, entity->id);
      Rectangle bounds = ContentBounds(p_camera, p_scene, entity);
      if (view &&
          view->active &&
          CheckCollisionPointRec(mouse, bounds)) {
        pointer_view = view;
        pointer_entity = entity;
        pointer_bounds = bounds;
      }
    }
  }

  if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
    p_surface->focused_entity_id = pointer_view ? pointer_view->entity_id : 0;
    for (int32 index = 0; index < CANVAS_MAX_WEB_VIEWS; ++index) {
      CefRefPtr<CanvasCefClient> client = state->clients[index];
      if (client && client->browser()) {
        client->browser()->GetHost()->SetFocus(
            pointer_view == &p_surface->views[index]);
      }
    }
  }

  Canvas_Web_View *focused_view =
      FindView(p_surface, p_surface->focused_entity_id);
  Canvas_Web_View *input_view = pointer_view ? pointer_view : focused_view;
  if (!input_view || !input_view->active) return FALSE;
  int32 view_index = ViewIndex(p_surface, input_view);
  CefRefPtr<CanvasCefClient> client = state->clients[view_index];
  CefRefPtr<CefBrowser> browser = client ? client->browser() : nullptr;
  const Canvas_Entity *input_entity = pointer_entity ?
      pointer_entity :
      FindSurfaceEntity(p_scene, input_view->entity_id);
  if (!browser || !input_entity) return pointer_view != nullptr;

  Rectangle bounds = pointer_view
      ? pointer_bounds
      : ContentBounds(p_camera, p_scene, input_entity);
  boolean inside = pointer_view == input_view;
  float scale_x = static_cast<float>(input_view->texture_width) /
      std::max(bounds.width, 1.0f);
  float scale_y = static_cast<float>(input_view->texture_height) /
      std::max(bounds.height, 1.0f);
  CefMouseEvent event;
  event.x = static_cast<int>((mouse.x - bounds.x) * scale_x);
  event.y = static_cast<int>((mouse.y - bounds.y) * scale_y);
  event.modifiers = 0;
  CefRefPtr<CefBrowserHost> host = browser->GetHost();
  host->SendMouseMoveEvent(event, !inside);
  if (inside && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
    host->SendMouseClickEvent(event, MBT_LEFT, false, 1);
  }
  if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT) &&
      p_surface->focused_entity_id == input_view->entity_id) {
    host->SendMouseClickEvent(event, MBT_LEFT, true, 1);
  }
  if (inside) {
    float wheel = GetMouseWheelMove();
    if (wheel != 0.0f) {
      boolean control_zoom =
          IsKeyDown(KEY_LEFT_CONTROL) ||
          IsKeyDown(KEY_RIGHT_CONTROL) ||
          IsKeyDown(KEY_LEFT_SUPER) ||
          IsKeyDown(KEY_RIGHT_SUPER);
      if (!control_zoom) {
        host->SendMouseWheelEvent(event, 0, static_cast<int>(wheel * 80.0f));
      }
    }
  }

  if (p_surface->focused_entity_id == input_view->entity_id) {
    int32 codepoint = GetCharPressed();
    while (codepoint > 0) {
      CefKeyEvent key;
      key.type = KEYEVENT_CHAR;
      key.windows_key_code = codepoint;
      key.native_key_code = codepoint;
      key.character = static_cast<char16_t>(codepoint);
      key.unmodified_character = static_cast<char16_t>(codepoint);
      host->SendKeyEvent(key);
      codepoint = GetCharPressed();
    }

    struct {
      int32 raylib_key;
      int32 browser_key;
    } keys[] = {
        {KEY_BACKSPACE, 0x08},
        {KEY_ENTER, 0x0D},
        {KEY_LEFT, 0x25},
        {KEY_UP, 0x26},
        {KEY_RIGHT, 0x27},
        {KEY_DOWN, 0x28},
        {KEY_DELETE, 0x2E},
    };
    for (const auto &key_mapping : keys) {
      if (!IsKeyPressed(key_mapping.raylib_key)) continue;
      CefKeyEvent key;
      key.windows_key_code = key_mapping.browser_key;
      key.native_key_code = key_mapping.browser_key;
      key.type = KEYEVENT_RAWKEYDOWN;
      host->SendKeyEvent(key);
      key.type = KEYEVENT_KEYUP;
      host->SendKeyEvent(key);
    }
  }
  if (inside &&
      (IsKeyDown(KEY_LEFT_CONTROL) ||
       IsKeyDown(KEY_RIGHT_CONTROL) ||
       IsKeyDown(KEY_LEFT_SUPER) ||
       IsKeyDown(KEY_RIGHT_SUPER)) &&
      GetMouseWheelMove() != 0.0f) {
    return FALSE;
  }
  return inside;
}

extern "C" void Canvas_Web_Surface_Sync(
    Canvas_Web_Surface *p_surface,
    const Canvas_Camera *p_camera,
    const Canvas_Scene *p_scene) {
  if (!p_surface->initialized) return;
  CefDoMessageLoopWork();
  ReconcileViews(p_surface, p_camera, p_scene);

  for (int32 index = 0; index < CANVAS_MAX_WEB_VIEWS; ++index) {
    Canvas_Web_View *view = &p_surface->views[index];
    if (!view->browser_created) continue;
    if (!view->texture_ready && IsWindowReady()) {
      Image image = {
          .data = view->p_pixels,
          .width = view->texture_width,
          .height = view->texture_height,
          .mipmaps = 1,
          .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
      };
      view->texture = LoadTextureFromImage(image);
      view->texture_ready = view->texture.id != 0 ? TRUE : FALSE;
      if (view->texture_ready) {
        SetTextureFilter(view->texture, TEXTURE_FILTER_BILINEAR);
      }
    }
    if (view->texture_ready && view->pixels_dirty) {
      UpdateTexture(view->texture, view->p_pixels);
      view->pixels_dirty = FALSE;
    }
    NativeState *state = static_cast<NativeState *>(p_surface->p_native);
    CefRefPtr<CanvasCefClient> client = state->clients[index];
    if (client && client->browser() &&
        view->entity_type == CANVAS_ENTITY_WEB_CONTENT) {
      if (!view->color_scheme_applied ||
          view->applied_dark_mode != p_surface->dark_mode) {
        ApplyColorScheme(client->browser(), p_surface->dark_mode);
        view->color_scheme_applied = TRUE;
        view->applied_dark_mode = p_surface->dark_mode;
      }
      view->can_go_back = client->browser()->CanGoBack() ? TRUE : FALSE;
      view->can_go_forward = client->browser()->CanGoForward() ? TRUE : FALSE;
    }
  }
}

extern "C" void Canvas_Web_Surface_Draw(
    const Canvas_Web_Surface *p_surface,
    const Canvas_Camera *p_camera,
    const Canvas_Scene *p_scene) {
  for (size_t index = 0;
      index < Dowa_Array_Length(p_scene->p_entities);
      ++index) {
    Canvas_Web_Surface_Draw_Entity(
        p_surface,
        p_camera,
        p_scene,
        index);
  }
}

extern "C" void Canvas_Web_Surface_Draw_Entity(
    const Canvas_Web_Surface *p_surface,
    const Canvas_Camera *p_camera,
    const Canvas_Scene *p_scene,
    size_t entity_index) {
  if (entity_index >= Dowa_Array_Length(p_scene->p_entities)) return;
  const Canvas_Entity *entity = &p_scene->p_entities[entity_index];
  if (entity->type != CANVAS_ENTITY_WEB_CONTENT &&
      entity->type != CANVAS_ENTITY_IMAGE) {
    return;
  }
  const Canvas_Web_View *view = FindView(p_surface, entity->id);
  if (!view || !view->active || !view->texture_ready) return;

  Rectangle bounds = ContentBounds(p_camera, p_scene, entity);
  float left = std::max(bounds.x, 0.0f);
  float top = std::max(bounds.y, 0.0f);
  float right = std::min(
      bounds.x + bounds.width,
      static_cast<float>(p_camera->viewport_width));
  float bottom = std::min(
      bounds.y + bounds.height,
      static_cast<float>(p_camera->viewport_height));
  if (right <= left || bottom <= top) return;

  BeginScissorMode(
      static_cast<int>(left),
      static_cast<int>(top),
      static_cast<int>(right - left),
      static_cast<int>(bottom - top));
  DrawTexturePro(
      view->texture,
      Rectangle{
          0.0f,
          0.0f,
          static_cast<float>(view->texture_width),
          static_cast<float>(view->texture_height),
      },
      bounds,
      Vector2{0.0f, 0.0f},
      0.0f,
      Color{
          255,
          255,
          255,
          static_cast<unsigned char>(EntityOpacity(entity) * 255.0f)});
  EndScissorMode();
}

extern "C" const char *Canvas_Web_Surface_URL(
    const Canvas_Web_Surface *p_surface,
    uint32 entity_id) {
  const Canvas_Web_View *view = FindView(p_surface, entity_id);
  return view && view->url[0] ? view->url : nullptr;
}

extern "C" boolean Canvas_Web_Surface_Can_Go_Back(
    const Canvas_Web_Surface *p_surface,
    uint32 entity_id) {
  const Canvas_Web_View *view = FindView(p_surface, entity_id);
  return view ? view->can_go_back : FALSE;
}

extern "C" boolean Canvas_Web_Surface_Can_Go_Forward(
    const Canvas_Web_Surface *p_surface,
    uint32 entity_id) {
  const Canvas_Web_View *view = FindView(p_surface, entity_id);
  return view ? view->can_go_forward : FALSE;
}

extern "C" void Canvas_Web_Surface_Go_Back(
    Canvas_Web_Surface *p_surface,
    uint32 entity_id) {
  if (!p_surface->p_native) return;
  Canvas_Web_View *view = FindView(p_surface, entity_id);
  if (!view) return;
  NativeState *state = static_cast<NativeState *>(p_surface->p_native);
  CefRefPtr<CanvasCefClient> client = state->clients[ViewIndex(p_surface, view)];
  if (client && client->browser() && client->browser()->CanGoBack()) {
    client->browser()->GoBack();
  }
}

extern "C" void Canvas_Web_Surface_Go_Forward(
    Canvas_Web_Surface *p_surface,
    uint32 entity_id) {
  if (!p_surface->p_native) return;
  Canvas_Web_View *view = FindView(p_surface, entity_id);
  if (!view) return;
  NativeState *state = static_cast<NativeState *>(p_surface->p_native);
  CefRefPtr<CanvasCefClient> client = state->clients[ViewIndex(p_surface, view)];
  if (client && client->browser() && client->browser()->CanGoForward()) {
    client->browser()->GoForward();
  }
}

extern "C" boolean Canvas_Web_Surface_Is_Focused(
    const Canvas_Web_Surface *p_surface) {
  return p_surface->focused_entity_id != 0;
}

extern "C" void Canvas_Web_Surface_Shutdown(Canvas_Web_Surface *p_surface) {
  if (!p_surface->p_native) return;
  NativeState *state = static_cast<NativeState *>(p_surface->p_native);
  for (int32 index = 0; index < CANVAS_MAX_WEB_VIEWS; ++index) {
    if (state->clients[index] && state->clients[index]->browser()) {
      state->clients[index]->browser()->GetHost()->CloseBrowser(true);
    }
    state->clients[index] = nullptr;
    if (p_surface->views[index].texture_ready) {
      UnloadTexture(p_surface->views[index].texture);
    }
    Dowa_Free(p_surface->views[index].p_pixels);
  }
  CefDoMessageLoopWork();
  if (state->cef_initialized) CefShutdown();
  state->~NativeState();
  p_surface->p_native = nullptr;
  p_surface->initialized = FALSE;
}