#include "yardim_cef.hpp" #include // GetCurrentProcessId/OpenProcess (süreç-başına CEF profili) #include #include #include #include #include "include/cef_app.h" #include "include/cef_browser.h" #include "include/cef_client.h" #include "include/cef_render_handler.h" #include "fiko/cekirdek/gecici.hpp" // merkezi geçici dizin (gecici/cef profili) #include "fiko/core/gunluk.hpp" namespace fiko { namespace { // Panelin istediği boyut (GetViewRect bunu döner). 0 → henüz panel çizilmedi. int g_istekG = 900, g_istekY = 620; // Son OnPaint karesi (RGBA8). Tek thread (pompa ana thread'de) → kilit gerekmez. std::vector g_piksel; int g_kareG = 0, g_kareY = 0; bool g_kareTaze = false; bool g_panelAcik = false; // GPU-hızlandırma: true → shared texture + OnAcceleratedPaint (GPU-GPU), disable-gpu- // compositing EKLENMEZ (shared texture GPU compositing ister). false → software OnPaint. bool g_gpuHizli = false; std::function g_paylasimKanca; // OnAcceleratedPaint → DinamikDoku köprüsü // Teşhis sayaçları: hangi paint yolu çalışıyor (GPU accel vs software). Panel boşken gösterilir. int g_accelSayac = 0, g_paintSayac = 0, g_accelKopyaOk = 0; std::string g_sonUrl; // otomatik software fallback'te yeniden açmak için class YardimClient : public CefClient, public CefLifeSpanHandler, public CefRenderHandler { public: CefRefPtr GetLifeSpanHandler() override { return this; } CefRefPtr GetRenderHandler() override { return this; } // ---- LifeSpan ---- void OnAfterCreated(CefRefPtr b) override { tarayici_ = b; } void OnBeforeClose(CefRefPtr) override { tarayici_ = nullptr; } // ---- Render (OSR) ---- void GetViewRect(CefRefPtr, CefRect& rect) override { rect.Set(0, 0, g_istekG > 0 ? g_istekG : 1, g_istekY > 0 ? g_istekY : 1); } // GPU-hızlı yol: CEF pikselleri paylaşımlı D3D11 texture'da verir → kanca GPU-GPU kopyalar // (CPU'ya hiç inmez). Handle bu callback SÜRESİNCE geçerli (keyed-mutex yok, kopya senkron). void OnAcceleratedPaint(CefRefPtr, PaintElementType tip, const RectList&, const CefAcceleratedPaintInfo& info) override { ++g_accelSayac; if (tip != PET_VIEW || !g_paylasimKanca || !info.shared_texture_handle) return; if (g_paylasimKanca((void*)info.shared_texture_handle, g_istekG, g_istekY)) ++g_accelKopyaOk; } void OnPaint(CefRefPtr, PaintElementType tip, const RectList&, const void* tampon, int g, int y) override { ++g_paintSayac; if (tip != PET_VIEW || !tampon || g <= 0 || y <= 0) return; // CEF BGRA verir → ImGui/Vulkan dokusu RGBA8 → kanal takası. const size_t n = (size_t)g * (size_t)y; g_piksel.resize(n * 4); const unsigned char* s = (const unsigned char*)tampon; unsigned char* d = g_piksel.data(); for (size_t i = 0; i < n; ++i) { d[i * 4 + 0] = s[i * 4 + 2]; // R <- B d[i * 4 + 1] = s[i * 4 + 1]; // G d[i * 4 + 2] = s[i * 4 + 0]; // B <- R d[i * 4 + 3] = s[i * 4 + 3]; // A } g_kareG = g; g_kareY = y; g_kareTaze = true; } CefRefPtr tarayici() { return tarayici_; } private: CefRefPtr tarayici_; IMPLEMENT_REFCOUNTING(YardimClient); }; CefRefPtr g_client; // CefApp: command-line switch'leri tek yerden (ANA + tüm alt-süreçler). Olmadan // (eski hâl) session-crashed şeridi kapatılamıyordu VE OSR bazı GPU sürücülerinde // gerçek pencereye düşüyordu (Fikri: "açar açmaz ayrı Chrome penceresi"). class YardimApp : public CefApp, public CefBrowserProcessHandler { public: CefRefPtr GetBrowserProcessHandler() override { return this; } void OnBeforeCommandLineProcessing(const CefString&, CefRefPtr cmd) override { // "Düzgün kapanmadı / geri yükle" şeridini ve tüm hata dialoglarını KAPAT — gömülü // görüntüleyicide kullanıcıya Chrome UI'ı gösterilmez. cmd->AppendSwitch("disable-session-crashed-bubble"); cmd->AppendSwitch("hide-crash-restore-bubble"); cmd->AppendSwitch("noerrdialogs"); cmd->AppendSwitch("disable-infobars"); cmd->AppendSwitch("disable-features=Translate,InfiniteSessionRestore"); if (!g_gpuHizli) { // SOFTWARE OSR: GPU compositing'i kapat (sürücü OSR'yi gerçek pencereye düşürmesin → // "açar açmaz ayrı Chrome" fix) + vsync kapat. cmd->AppendSwitch("disable-gpu-compositing"); cmd->AppendSwitch("disable-gpu-vsync"); } else { // ACCELERATED OSR: shared texture GPU'ya MUHTAÇ. OSR'de GPU varsayılan kapalı/ // blocklist olabilir → hiç paint gelmez (accel=0 paint=0 ölçüldü). GPU'yu ZORLA + // ANGLE D3D11 (shared texture D3D11 device ister) + rasterization. cmd->AppendSwitch("enable-gpu"); cmd->AppendSwitch("ignore-gpu-blocklist"); cmd->AppendSwitch("enable-gpu-rasterization"); cmd->AppendSwitchWithValue("use-angle", "d3d11"); } } // İkinci bir fikocad aynı root_cache_path ile açılırsa CEF relaunch'ı BU (ilk) sürece // iletir; false dönülürse VARSAYILAN davranış boş bir Chrome penceresi açmaktır // (yaşandı: "ikinci fikocad Chrome'da açılıyor"). true = ele alındı, pencere yok. bool OnAlreadyRunningAppRelaunch(CefRefPtr, const CefString&) override { gunluk::uyari("CEF: ikinci kopya relaunch algılandı (aynı profil) — bastırıldı"); return true; } IMPLEMENT_REFCOUNTING(YardimApp); }; CefRefPtr g_app; // oto_kayit.cpp'deki kalıbın aynısı: erişilemiyor ≈ ölü (yanlış pozitif zararsız — dizin kalır). bool surecCanliMi(unsigned long pid) { if (pid == 0) return false; HANDLE h = OpenProcess(SYNCHRONIZE, FALSE, (DWORD)pid); if (!h) return false; const bool canli = WaitForSingleObject(h, 0) == WAIT_TIMEOUT; CloseHandle(h); return canli; } // Süreç-başına CEF profil SLOTU: gecici/cef/s. CEF 120+ root_cache_path üstünde // süreç-singleton kilidi tutar — iki fikocad AYNI dizini paylaşırsa ikinci CefInitialize // ilk sürece relaunch iletir ve ilk süreçte Chrome penceresi açılır. Slot bunu ayrıştırır; // pid'li dizinden farkı YENİDEN KULLANIM: her açılışta sıfırdan profil kurmak + // senkron remove_all süpürgesi açılışı donduruyordu (pencere gelmeden beyaz bekleme). // İsimli mutex slotu canlı süreçler arasında kilitler; süreç ölünce kendiliğinden düşer. HANDLE g_slotKilit = nullptr; // süreç ömrünce tutulur (yardimCefBitir'de bırakılır) std::filesystem::path cefProfilDizini() { const std::filesystem::path kok = gecici::altDizin("cef"); // Eski pid'li düzenden kalan dizinleri ARKA PLANDA topla (bir kerelik göç; slot // dizinleri sayısal olmadığından dokunulmaz). Ana thread'i bloke etmez. std::thread([kok] { std::error_code ec; for (const auto& g : std::filesystem::directory_iterator(kok, ec)) { if (!g.is_directory(ec)) continue; const std::wstring ad = g.path().filename().wstring(); if (ad.empty() || ad.find_first_not_of(L"0123456789") != std::wstring::npos) continue; if (!surecCanliMi(wcstoul(ad.c_str(), nullptr, 10))) std::filesystem::remove_all(g.path(), ec); // kilitliyse sonraki açılış toplar } }).detach(); for (int i = 1; i <= 8; ++i) { HANDLE h = CreateMutexW(nullptr, TRUE, (L"fikocad_cef_slot_" + std::to_wstring(i)).c_str()); if (h && GetLastError() != ERROR_ALREADY_EXISTS) { g_slotKilit = h; return kok / (L"s" + std::to_wstring(i)); } if (h) CloseHandle(h); // slot başka kopyada } return kok / std::to_wstring(GetCurrentProcessId()); // 8+ kopya: pid'e düş } CefRefPtr host() { if (!g_client) return nullptr; CefRefPtr b = g_client->tarayici(); return b ? b->GetHost() : nullptr; } } // namespace void yardimCefGpuHizliAyarla(bool acik) { g_gpuHizli = acik; } bool yardimCefGpuHizli() { return g_gpuHizli; } void yardimCefPaylasimKanca(std::function fn) { g_paylasimKanca = std::move(fn); } int yardimCefAltSurec(HINSTANCE hInst) { CefMainArgs args(hInst); // App alt-süreçte de geçilmeli: switch'ler (OSR/crash-bubble) render/gpu süreçlerine // de uygulanır. Aksi halde GPU süreci compositing açıp OSR'yi bozabilir. CefRefPtr app = new YardimApp(); return CefExecuteProcess(args, app.get(), nullptr); } void yardimCefBaslat(HINSTANCE hInst) { CefMainArgs args(hInst); CefSettings settings; settings.no_sandbox = true; // OSR: CEF kendi penceresini AÇMAZ, pikselleri OnPaint ile verir. settings.windowless_rendering_enabled = true; // Tek-thread: OnPaint + fare olayları ana thread'de (her kare yardimCefPompa). settings.multi_threaded_message_loop = false; // Kaynak yolu AYARLANMAZ: icudtl.dat/*.pak/locales exe YANINA kopyalanır (kanonik CEF // düz yerleşimi). resources_dir_path ICU init'ten SONRA uygulandığı için // "Invalid file descriptor to ICU data" fast-fail'ine yol açıyordu (07-17 ölçüldü). // Tarayıcı profili merkezi geçici ağaçta, kopya başına SLOT (gecici/cef/s) — // exe yanına klasör saçılmaz; ikinci fikocad kopyası singleton kilidine takılmaz. CefString(&settings.root_cache_path).FromWString(cefProfilDizini().wstring()); g_app = new YardimApp(); if (CefInitialize(args, settings, g_app.get(), nullptr)) g_client = new YardimClient(); } void yardimCefBitir() { if (!g_client) return; if (CefRefPtr h = host()) h->CloseBrowser(true); CefDoMessageLoopWork(); // kapanışın işlenmesine izin ver g_client = nullptr; g_app = nullptr; CefShutdown(); // Profil dizini SİLİNMEZ (sıcak yeniden kullanım); yalnız slot kilidi bırakılır. if (g_slotKilit) { ReleaseMutex(g_slotKilit); CloseHandle(g_slotKilit); g_slotKilit = nullptr; } } void yardimCefPompa() { if (!g_client) return; // Shared texture OSR external begin-frame: her pompada "yeni kare çiz" tetikle → CEF // OnAcceleratedPaint üretir (otomatik scheduling bu modda çalışmaz). if (g_gpuHizli) if (CefRefPtr h = host()) h->SendExternalBeginFrame(); CefDoMessageLoopWork(); } void yardimCefAc(const std::string& dosyaYolu) { if (!g_client) return; g_panelAcik = true; std::string url = dosyaYolu; for (char& c : url) { if (c == '\\') c = '/'; } url = "file:///" + url; g_sonUrl = url; if (CefRefPtr h = host()) { // tarayıcı yaşıyor → sadece git h->GetBrowser()->GetMainFrame()->LoadURL(url); return; } CefWindowInfo info; info.SetAsWindowless(nullptr); // penceresiz (OSR) // GPU-hızlı: CEF pikselleri paylaşımlı GPU texture olarak versin → OnAcceleratedPaint. info.shared_texture_enabled = g_gpuHizli ? 1 : 0; // Shared texture OSR'de CEF otomatik frame üretmez (ölçüldü: shared_texture=1 → hiç paint). // External begin-frame: her kare SendExternalBeginFrame ile "çiz" tetiklenir. info.external_begin_frame_enabled = g_gpuHizli ? 1 : 0; CefBrowserSettings bs; bs.windowless_frame_rate = 30; CefBrowserHost::CreateBrowser(info, g_client, url, bs, nullptr, nullptr); } bool yardimCefAcikMi() { return g_panelAcik; } void yardimCefKapat() { g_panelAcik = false; } bool* yardimCefAcikBayrak() { return &g_panelAcik; } void yardimCefBoyut(int g, int y) { if (g <= 0 || y <= 0) return; if (g == g_istekG && y == g_istekY) return; g_istekG = g; g_istekY = y; if (CefRefPtr h = host()) h->WasResized(); } void yardimCefSoftwareGec() { if (!g_gpuHizli || !g_client) return; g_gpuHizli = false; // artık OnPaint (software) yolu // Mevcut GPU browser'ı kapat + software (shared_texture=0) browser'ı yeniden aç. g_client // aynı kalır; yeni browser OnAfterCreated'da ona bağlanır. Kapanış işlensin diye pompa. if (CefRefPtr h = host()) h->CloseBrowser(true); for (int i = 0; i < 20; ++i) CefDoMessageLoopWork(); CefWindowInfo info; info.SetAsWindowless(nullptr); info.shared_texture_enabled = 0; info.external_begin_frame_enabled = 0; CefBrowserSettings bs; bs.windowless_frame_rate = 30; CefBrowserHost::CreateBrowser(info, g_client, g_sonUrl, bs, nullptr, nullptr); } void yardimCefTeshis(int* accel, int* paint, int* kopyaOk) { if (accel) *accel = g_accelSayac; if (paint) *paint = g_paintSayac; if (kopyaOk) *kopyaOk = g_accelKopyaOk; } void yardimCefYenile() { // CEF'i yeniden paint'e zorla. GPU-hızlı yolda İLK OnAcceleratedPaint kanca set edilmeden // (güncelle fazı) gelip kaçabiliyor; statik doküman tekrar paint üretmez → panel boş kalır. // Doku boşken bunu çağır → yeni paint gelir, kanca (artık set) yakalar. if (CefRefPtr h = host()) h->Invalidate(PET_VIEW); } bool yardimCefKareAl(const unsigned char** rgba, int* g, int* y) { if (!g_kareTaze || g_piksel.empty()) return false; *rgba = g_piksel.data(); *g = g_kareG; *y = g_kareY; g_kareTaze = false; return true; } namespace { // YardimFareDurum → CEF EVENTFLAG maskesi. Basılı düğme bayrağı olmadan CEF sürükleme // (scrollbar tutma, metin seçme) BAŞLATMAZ. u32 bayrak(const YardimFareDurum& d) { u32 m = 0; if (d.sol) m |= EVENTFLAG_LEFT_MOUSE_BUTTON; if (d.sag) m |= EVENTFLAG_RIGHT_MOUSE_BUTTON; if (d.orta) m |= EVENTFLAG_MIDDLE_MOUSE_BUTTON; if (d.shift) m |= EVENTFLAG_SHIFT_DOWN; if (d.ctrl) m |= EVENTFLAG_CONTROL_DOWN; if (d.alt) m |= EVENTFLAG_ALT_DOWN; return m; } } // namespace void yardimCefFareHareket(int x, int y, bool ayrildi, const YardimFareDurum& d) { CefRefPtr h = host(); if (!h) return; CefMouseEvent e; e.x = x; e.y = y; e.modifiers = bayrak(d); h->SendMouseMoveEvent(e, ayrildi); } void yardimCefFareTik(int x, int y, bool sagMi, bool birakildi, int tikSayi, const YardimFareDurum& d) { CefRefPtr h = host(); if (!h) return; CefMouseEvent e; e.x = x; e.y = y; e.modifiers = bayrak(d); h->SendMouseClickEvent(e, sagMi ? MBT_RIGHT : MBT_LEFT, birakildi, tikSayi > 0 ? tikSayi : 1); } void yardimCefFareTeker(int x, int y, int deltaX, int deltaY, const YardimFareDurum& d) { CefRefPtr h = host(); if (!h) return; CefMouseEvent e; e.x = x; e.y = y; e.modifiers = bayrak(d); h->SendMouseWheelEvent(e, deltaX, deltaY); } void yardimCefOdak(bool odakli) { if (CefRefPtr h = host()) h->SetFocus(odakli); } } // namespace fiko