diff --git a/native/host.cpp b/native/host.cpp index 9278498..6217ea2 100644 --- a/native/host.cpp +++ b/native/host.cpp @@ -123,7 +123,7 @@ template struct Handler { T* acquire(){api.base.add_ref(&api.base);return &api;} }; struct View { - std::string key,plugin,title;int width=600,height=840,render_scale=1;bool dashboard=false,shown=false,dirty=false,closing=false,closed=false; + std::string key,plugin,title;int width=600,height=840;float render_scale=1.f;bool dashboard=false,shown=false,dirty=false,closing=false,closed=false; OverlayPlacement placement;NotificationClick notification_click;LauncherPointer launcher_pointer;std::chrono::steady_clock::time_point haptic_at{},activation_at{}; cef_mouse_event_t pointer{};bool pointer_valid=false,stick_scrolling=false;vr::TrackedDeviceIndex_t pointer_device=vr::k_unTrackedDeviceIndexInvalid;std::chrono::steady_clock::time_point pointer_at{}; cef_file_dialog_callback_t* file_dialog=nullptr;std::string file_dialog_id;bool file_multiple=false,file_directory=false; @@ -281,9 +281,9 @@ static void anchor_notification(View& v){ } static View& create_view(const std::string& key,const std::string& title,const std::string& url,bool dock,int width,int height,const std::string& plugin="",float physical_width=0){ if(views.count(key)){auto& v=*views.at(key);if(dock&&key!="launcher")overlays->ShowDashboard(key.c_str());else if(key!="notifications"&&key!="launcher")show(v,true);return v;} - // Use 3x for launcher artwork and 2x for scrolling panels. CEF readback, + // Use 1.5x for launcher artwork and 2x for scrolling panels. CEF readback, // texture upload and launcher mipmap generation scale with the pixel count. - if(views.size()>=34)throw std::runtime_error("Window limit exceeded");auto v=std::make_unique();v->key=key;v->plugin=plugin;v->title=title;v->dashboard=dock;v->width=width;v->height=height;v->render_scale=key=="launcher"?3:(key=="framely.manager"||key=="menu"||key=="notifications")?2:1; + if(views.size()>=34)throw std::runtime_error("Window limit exceeded");auto v=std::make_unique();v->key=key;v->plugin=plugin;v->title=title;v->dashboard=dock;v->width=width;v->height=height;v->render_scale=key=="launcher"?1.5f:(key=="framely.manager"||key=="menu"||key=="notifications")?2:1; auto error=dock?overlays->CreateDashboardOverlay(key.c_str(),title.c_str(),&v->overlay,&v->thumbnail):overlays->CreateOverlay(key.c_str(),title.c_str(),&v->overlay);if(error)throw std::runtime_error("Overlay creation failed: "+std::to_string(error)); overlays->SetOverlayInputMethod(v->overlay,key=="notifications"?vr::VROverlayInputMethod_None:vr::VROverlayInputMethod_Mouse);vr::HmdVector2_t scale{{float(width),float(height)}};overlays->SetOverlayMouseScale(v->overlay,&scale);overlays->SetOverlayFlag(v->overlay,vr::VROverlayFlags_HideLaserIntersection,false);overlays->SetOverlayFlag(v->overlay,vr::VROverlayFlags_SendVRSmoothScrollEvents,true);overlays->SetOverlayFlag(v->overlay,vr::VROverlayFlags_VisibleInDashboard,true);overlays->SetOverlayFlag(v->overlay,vr::VROverlayFlags_MakeOverlaysInteractiveIfVisible,true);overlays->SetOverlayFlag(v->overlay,vr::VROverlayFlags_SortWithNonSceneOverlays,false);overlays->SetOverlaySortOrder(v->overlay,0); diff --git a/native/render_resolution.h b/native/render_resolution.h index 6619103..454a32f 100644 --- a/native/render_resolution.h +++ b/native/render_resolution.h @@ -1,6 +1,6 @@ #pragma once // CEF view/input coordinates stay in DIP; only the paint buffer uses this scale. -inline int render_screen_info(cef_screen_info_t* info,int width,int height,int scale){ +inline int render_screen_info(cef_screen_info_t* info,int width,int height,float scale){ info->size=sizeof(*info);info->device_scale_factor=float(scale);info->depth=32;info->depth_per_component=8;info->is_monochrome=0; info->rect={0,0,width,height};info->available_rect=info->rect;return 1; } diff --git a/tests/browser_probe.cpp b/tests/browser_probe.cpp index b14325a..e925e23 100644 --- a/tests/browser_probe.cpp +++ b/tests/browser_probe.cpp @@ -42,9 +42,9 @@ int main(int argc,char** argv){ cef_main_args_t args{argc,argv};if(std::strcmp(cef_api_hash(CEF_API_VERSION,0),CEF_API_HASH_PLATFORM))return 2;int child=cef_execute_process(&args,nullptr,nullptr);if(child>=0)return child;if(url.empty()||runtime.empty()||cef.empty())return 2; cef_settings_t settings{};settings.size=sizeof(settings);settings.windowless_rendering_enabled=1;str(settings.root_cache_path,runtime+"/cache");str(settings.log_file,runtime+"/cef.log");str(settings.resources_dir_path,cef+"/Resources");str(settings.locales_dir_path,cef+"/Resources/locales"); if(!cef_initialize(&args,&settings,nullptr,nullptr))return 3; - render.api.get_view_rect=[](cef_render_handler_t*,cef_browser_t*,cef_rect_t* r){*r=launcher_mode?cef_rect_t{0,0,1440,800}:std::getenv("FRAMELY_STORE_TEST")?cef_rect_t{0,0,1440,810}:cef_rect_t{0,0,600,notification_mode?360:840};};render.api.get_screen_info=[](cef_render_handler_t*,cef_browser_t*,cef_screen_info_t* info){if(launcher_mode)return render_screen_info(info,1440,800,3);return render_screen_info(info,std::getenv("FRAMELY_STORE_TEST")?1440:600,std::getenv("FRAMELY_STORE_TEST")?810:(notification_mode?360:840),std::getenv("FRAMELY_STORE_TEST")?4:2);};render.api.on_paint=[](cef_render_handler_t*,cef_browser_t*,cef_paint_element_type_t type,size_t dirty_count,const cef_rect_t* dirty,const void* data,int w,int h){if(type!=PET_VIEW)return;++paints;if(w==(launcher_mode?4320:std::getenv("FRAMELY_STORE_TEST")?5760:1200)&&h==(launcher_mode?2400:std::getenv("FRAMELY_STORE_TEST")?3240:(notification_mode?720:1680)))scaled_paint=true;pixel_width=w;pixel_height=h;merge_paint(pixels,data,w,h,dirty_count,dirty);// Semi-transparent charcoal panels still need a readable surface and clear rounded corners. + render.api.get_view_rect=[](cef_render_handler_t*,cef_browser_t*,cef_rect_t* r){*r=launcher_mode?cef_rect_t{0,0,1440,800}:std::getenv("FRAMELY_STORE_TEST")?cef_rect_t{0,0,1440,810}:cef_rect_t{0,0,600,notification_mode?360:840};};render.api.get_screen_info=[](cef_render_handler_t*,cef_browser_t*,cef_screen_info_t* info){if(launcher_mode)return render_screen_info(info,1440,800,1.5f);return render_screen_info(info,std::getenv("FRAMELY_STORE_TEST")?1440:600,std::getenv("FRAMELY_STORE_TEST")?810:(notification_mode?360:840),std::getenv("FRAMELY_STORE_TEST")?4:2);};render.api.on_paint=[](cef_render_handler_t*,cef_browser_t*,cef_paint_element_type_t type,size_t dirty_count,const cef_rect_t* dirty,const void* data,int w,int h){if(type!=PET_VIEW)return;++paints;if(w==(launcher_mode?2160:std::getenv("FRAMELY_STORE_TEST")?5760:1200)&&h==(launcher_mode?1200:std::getenv("FRAMELY_STORE_TEST")?3240:(notification_mode?720:1680)))scaled_paint=true;pixel_width=w;pixel_height=h;merge_paint(pixels,data,w,h,dirty_count,dirty);// Semi-transparent charcoal panels still need a readable surface and clear rounded corners. if(!notification_mode&&w>0&&h>0){auto alpha=[&](int x,int y){return pixels[(y*w+x)*4+3];};if(alpha(0,0)==0&&alpha(w-1,0)==0&&alpha(0,h-1)==0&&alpha(w-1,h-1)==0&&alpha(w/2,40)>200)rounded_paint=true;}}; - display.api.on_console_message=[](cef_display_handler_t*,cef_browser_t* b,cef_log_severity_t,const cef_string_t* m,const cef_string_t*,int){auto s=text(m);std::cout<get_host(b);cef_mouse_event_t mouse{};mouse.x=x;mouse.y=y;host->send_mouse_move_event(host,&mouse,0);host->send_mouse_wheel_event(host,&mouse,dx,dy);host->base.release(&host->base);}}if(s.rfind("FRAMELY_NATIVE_KEYBOARD_",0)==0){auto* frame=b->get_focused_frame(b);auto command=s.substr(24);if(command=="TEXT")keyboard_edit(frame,"汉😃");else if(command=="BACKSPACE")keyboard_edit(frame,"\b");else if(command=="ENTER")keyboard_edit(frame,"\n");else if(command=="X")keyboard_edit(frame,"x");else if(command=="Y")keyboard_edit(frame,"y");else if(command=="LEFT")keyboard_edit(frame,"\x1b[D");else if(command=="RIGHT")keyboard_edit(frame,"\x1b[C");frame->base.release(&frame->base);}if(s=="FRAMELY_LAUNCHER_CANCEL_PRESS"){auto* pointer_host=b->get_host(b);cef_mouse_event_t outside{};outside.modifiers=EVENTFLAG_LEFT_MOUSE_BUTTON;pointer_host->send_mouse_move_event(pointer_host,&outside,0);outside.modifiers=0;pointer_host->send_mouse_click_event(pointer_host,&outside,MBT_LEFT,1,1);test_pointer.clear();press_pending=false;auto* frame=b->get_main_frame(b);cef_string_t code{},source{};str(code,"window.dispatchEvent(new Event('framely.launcher.cancelInput'))");frame->execute_java_script(frame,&code,&source,1);cef_string_utf16_clear(&code);frame->base.release(&frame->base);pointer_host->base.release(&pointer_host->base);}if(s.rfind("FRAMELY_LAUNCHER_DRAG_",0)==0){int x=0,y=0;if(press_pending&&std::sscanf(s.c_str(),"FRAMELY_LAUNCHER_DRAG_%d_%d",&x,&y)==2){press_dx=x-test_pointer.x;press_dy=y-test_pointer.y;}}if(s.rfind("FRAMELY_LAUNCHER_PRESS_",0)==0){int x=0,y=0;if(std::sscanf(s.c_str(),"FRAMELY_LAUNCHER_PRESS_%d_%d_%d_%d_%d",&x,&y,&press_dx,&press_dy,&press_duration)==5){auto* pointer_host=b->get_host(b);test_pointer.begin(x,y,std::chrono::steady_clock::now());press_pending=true;cef_mouse_event_t mouse{};mouse.x=x;mouse.y=y;pointer_host->set_focus(pointer_host,1);pointer_host->send_mouse_move_event(pointer_host,&mouse,0);mouse.modifiers=EVENTFLAG_LEFT_MOUSE_BUTTON;pointer_host->send_mouse_click_event(pointer_host,&mouse,MBT_LEFT,0,1);pointer_host->base.release(&pointer_host->base);}}if(s.rfind("FRAMELY_LAUNCHER_HOVER_",0)==0){int x=0,y=0;if(std::sscanf(s.c_str(),"FRAMELY_LAUNCHER_HOVER_%d_%d",&x,&y)==2){auto* hover_host=b->get_host(b);cef_mouse_event_t mouse{};mouse.x=x;mouse.y=y;hover_host->send_mouse_move_event(hover_host,&mouse,0);hover_host->base.release(&hover_host->base);}}if(s.rfind("FRAMELY_LAUNCHER_REGION_",0)==0){int x=0,y=0,w=0,h=0;if(std::sscanf(s.c_str(),"FRAMELY_LAUNCHER_REGION_%d_%d_%d_%d",&x,&y,&w,&h)==4){const int scale=launcher_mode?3:4;int ink=0;for(int py=std::max(0,y*scale);py128)ink++;if(ink<30){failed=true;std::cerr<<"FRAMELY_BRIDGE_FAIL missing painted control "<128&&pixels[i]>180&&pixels[i+1]>180&&pixels[i+2]>180)ink++;}if(ink<30){failed=true;std::cerr<<"FRAMELY_BRIDGE_FAIL missing painted label "<get_host(b);cef_mouse_event_t mouse{};mouse.x=x;mouse.y=y;host->send_mouse_move_event(host,&mouse,0);host->send_mouse_wheel_event(host,&mouse,dx,dy);host->base.release(&host->base);}}if(s.rfind("FRAMELY_NATIVE_KEYBOARD_",0)==0){auto* frame=b->get_focused_frame(b);auto command=s.substr(24);if(command=="TEXT")keyboard_edit(frame,"汉😃");else if(command=="BACKSPACE")keyboard_edit(frame,"\b");else if(command=="ENTER")keyboard_edit(frame,"\n");else if(command=="X")keyboard_edit(frame,"x");else if(command=="Y")keyboard_edit(frame,"y");else if(command=="LEFT")keyboard_edit(frame,"\x1b[D");else if(command=="RIGHT")keyboard_edit(frame,"\x1b[C");frame->base.release(&frame->base);}if(s=="FRAMELY_LAUNCHER_CANCEL_PRESS"){auto* pointer_host=b->get_host(b);cef_mouse_event_t outside{};outside.modifiers=EVENTFLAG_LEFT_MOUSE_BUTTON;pointer_host->send_mouse_move_event(pointer_host,&outside,0);outside.modifiers=0;pointer_host->send_mouse_click_event(pointer_host,&outside,MBT_LEFT,1,1);test_pointer.clear();press_pending=false;auto* frame=b->get_main_frame(b);cef_string_t code{},source{};str(code,"window.dispatchEvent(new Event('framely.launcher.cancelInput'))");frame->execute_java_script(frame,&code,&source,1);cef_string_utf16_clear(&code);frame->base.release(&frame->base);pointer_host->base.release(&pointer_host->base);}if(s.rfind("FRAMELY_LAUNCHER_DRAG_",0)==0){int x=0,y=0;if(press_pending&&std::sscanf(s.c_str(),"FRAMELY_LAUNCHER_DRAG_%d_%d",&x,&y)==2){press_dx=x-test_pointer.x;press_dy=y-test_pointer.y;}}if(s.rfind("FRAMELY_LAUNCHER_PRESS_",0)==0){int x=0,y=0;if(std::sscanf(s.c_str(),"FRAMELY_LAUNCHER_PRESS_%d_%d_%d_%d_%d",&x,&y,&press_dx,&press_dy,&press_duration)==5){auto* pointer_host=b->get_host(b);test_pointer.begin(x,y,std::chrono::steady_clock::now());press_pending=true;cef_mouse_event_t mouse{};mouse.x=x;mouse.y=y;pointer_host->set_focus(pointer_host,1);pointer_host->send_mouse_move_event(pointer_host,&mouse,0);mouse.modifiers=EVENTFLAG_LEFT_MOUSE_BUTTON;pointer_host->send_mouse_click_event(pointer_host,&mouse,MBT_LEFT,0,1);pointer_host->base.release(&pointer_host->base);}}if(s.rfind("FRAMELY_LAUNCHER_HOVER_",0)==0){int x=0,y=0;if(std::sscanf(s.c_str(),"FRAMELY_LAUNCHER_HOVER_%d_%d",&x,&y)==2){auto* hover_host=b->get_host(b);cef_mouse_event_t mouse{};mouse.x=x;mouse.y=y;hover_host->send_mouse_move_event(hover_host,&mouse,0);hover_host->base.release(&hover_host->base);}}if(s.rfind("FRAMELY_LAUNCHER_REGION_",0)==0){int x=0,y=0,w=0,h=0;if(std::sscanf(s.c_str(),"FRAMELY_LAUNCHER_REGION_%d_%d_%d_%d",&x,&y,&w,&h)==4){const float scale=launcher_mode?1.5f:4.f;int ink=0;for(int py=std::max(0,int(y*scale));py128)ink++;if(ink<30){failed=true;std::cerr<<"FRAMELY_BRIDGE_FAIL missing painted control "<128&&pixels[i]>180&&pixels[i+1]>180&&pixels[i+2]>180)ink++;}if(ink<30){failed=true;std::cerr<<"FRAMELY_BRIDGE_FAIL missing painted label "<get_host(b);cef_mouse_event_t mouse{};mouse.x=50;mouse.y=45;host->set_focus(host,1);host->send_mouse_move_event(host,&mouse,0);host->send_mouse_click_event(host,&mouse,MBT_LEFT,0,1);host->send_mouse_click_event(host,&mouse,MBT_LEFT,1,1);host->base.release(&host->base);}if(s=="FRAMELY_DPI_CLICK_PASS")dpi_click=true;if(s.rfind("FRAMELY_PREVIEW_INSTALL_",0)==0)snapshot(s.substr(16));if(s=="FRAMELY_PREVIEW_STORE")snapshot("store");if(s=="FRAMELY_PREVIEW_FAVORITES")snapshot("favorites");if(s=="FRAMELY_PREVIEW_INSTALLED")snapshot("installed");if(s=="FRAMELY_PREVIEW_SETTINGS")snapshot("settings");if(s=="FRAMELY_VIEW_READY")ready=true;if(s=="FRAMELY_BRIDGE_PASS")passed=true;if(s.rfind("FRAMELY_BRIDGE_FAIL",0)==0)failed=true;return 1;};life.api.on_before_close=[](cef_life_span_handler_t*,cef_browser_t*){closed=true;}; client.api.get_render_handler=[](cef_client_t*){return render.acquire();};client.api.get_display_handler=[](cef_client_t*){return display.acquire();};client.api.get_life_span_handler=[](cef_client_t*){return life.acquire();}; cef_window_info_t win{};win.size=sizeof(win);win.windowless_rendering_enabled=1;cef_browser_settings_t bs{};bs.size=sizeof(bs);bs.windowless_frame_rate=60;cef_string_t uri{};str(uri,url);auto* browser=cef_browser_host_create_browser_sync(&win,&client.api,&uri,&bs,nullptr,nullptr);cef_string_utf16_clear(&uri);if(!browser)return 4;auto* host=browser->get_host(browser);