Using CEF3 with wxWidgets (and GTK) on Linux - c++

I started with this branch of wxWidgets from GitHub user hokein since it seems to do what I'm looking for with older versions of CEF. Basically, I'm trying to implement wxWebViewChromium (a wxWebView using CEF as a back-end) using cef_3.2526.1354 since this is the version we want to use in our application. I'm running CentOS 7 with GNOME in a VM using Hyper-V.
Original code from the repository linked above (part of webview_chromium.cpp)
#ifdef __WXGTK__
m_widget = gtk_scrolled_window_new( NULL, NULL );
g_object_ref( m_widget );
GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW( m_widget );
// Hide the scroll bar.
gtk_scrolled_window_set_policy( scrolled_window, GTK_POLICY_NEVER, GTK_POLICY_NEVER);
GtkWidget* view_port = gtk_viewport_new( NULL, NULL );
gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(scrolled_window),
view_port );
info.SetAsChild( view_port );
m_parent->DoAddChild( this );
PostCreation( size );
gtk_widget_show( view_port );
#endif
When I tried to compile and link this with cef_3.2526.1354, I was getting errors related to the info.SetAsChild(viewport); line. I was passing it a GtkWidget* and it was expecting cef_window_handle_t, CefRect.
What I've tried so far
I created the CefRect like this based on the GtkWidget* view_port:
GtkAllocation gtk_alloc;
gtk_widget_get_allocation(view_port, &gtk_alloc);
CefRect cef_rect (
(int)gtk_alloc.x,
(int)gtk_alloc.y,
(int)gtk_alloc.width,
(int)gtk_alloc.height
);
The typedef from the CEF library code indicates that cef_window_handle_t is an unsigned long, but the CEF documentation says it's a GtkWidget*... Which I guess are the same thing, but this led to some initial confusion. At first I thought that I needed to pass the X11 window id (XID) from the underlying X11 window, but this produced a runtime error:
Gdk: gdkdrawable-x11.c:952 drawable is not a pixmap or window
Simply casting the GtkWidget* as an unsigned long instead of using the underlying X11 window ID removed this error, so I think that this is what the function was expecting.
My code currently (part of webview_chromium.cpp)
#ifdef __WXGTK__
m_widget = gtk_scrolled_window_new( NULL, NULL );
g_object_ref( m_widget );
GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW( m_widget );
// Hide the scroll bar.
gtk_scrolled_window_set_policy( scrolled_window, GTK_POLICY_NEVER, GTK_POLICY_NEVER);
GtkWidget* view_port = gtk_viewport_new( NULL, NULL );
gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(scrolled_window),
view_port );
GtkAllocation gtk_alloc;
gtk_widget_get_allocation(view_port, &gtk_alloc);
CefRect cef_rect (
(int)gtk_alloc.x,
(int)gtk_alloc.y,
(int)gtk_alloc.width,
(int)gtk_alloc.height
);
info.SetAsChild(
(unsigned long) view_port,
cef_rect
);
m_parent->DoAddChild( this );
PostCreation( size );
// gtk_widget_show(view_port);
gtk_widget_show_all(m_widget);
gtk_widget_show_all(view_port);
#endif
What's happening
I am able to compile, link, and run the application. I can see the wxWidgets window, and I can minimize, maximize, resize, and close the window just like any normal window. There is nothing in the window - it is just a plain, grey, empty window. I am expecting (or hoping) to see the CEF browser. In the terminal that I used to launch the application, I get the following message:
[0727/132200:ERROR:browser_main_loop.cc(203)] Running without SUID sandbox! See https://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment for more information on developing with sandbox on.
This URL no longer seems to be of any use. If I launch ./cefclient --no-sandbox from the command line I get the same error message, but the CEF client works as expected. If I launch ./cefclient from the command line (without disabling sandbox), I get this same message except instead of ERROR it says FATAL and the CEF client does not work (crashes outright).
My questions
Have I done anything wrong with my modifications to the webview_chromium.cpp file? Is there anywhere else in this file that I should look for things that I might need to update to get this working with cef_3.2526.1354? How can I troubleshoot this empty window that I'm seeing?
Thanks!
UPDATE
Czarek Tomczak informed me that this version of CEF is expecting an X11 window handle, so I have modified my code as follows.
#ifdef __WXGTK__
m_widget = gtk_scrolled_window_new( NULL, NULL );
g_object_ref( m_widget );
GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW( m_widget );
// Hide the scroll bar.
gtk_scrolled_window_set_policy( scrolled_window, GTK_POLICY_NEVER, GTK_POLICY_NEVER);
GtkWidget* view_port = gtk_viewport_new( NULL, NULL );
gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(scrolled_window),
view_port );
gtk_widget_show_all(m_widget);
gtk_widget_show_all(view_port);
GtkAllocation gtk_alloc;
gtk_widget_get_allocation(view_port, &gtk_alloc);
CefRect cef_rect (
(int)gtk_alloc.x,
(int)gtk_alloc.y,
(int)gtk_alloc.width,
(int)gtk_alloc.height
);
info.SetAsChild(
gdk_x11_drawable_get_xid(gtk_widget_get_window(view_port)),
cef_rect
);
m_parent->DoAddChild( this );
PostCreation( size );
#endif
But this is still leading to the run-time error below, with the same blank window output as before.
Gdk: gdkdrawable-x11.c:952 drawable is not a pixmap or window
Any help pointing me in the right direction would be much appreciated. I have also tried creating an actual GtkWindow here instead of a scrolled window with a viewport inside it. This gets rid of the above run-time error, but it opens a second window, both of which are blank. I need all of this to work within one window.
UPDATE 2
With some help from Czarek Tomczak, the links he posted, and this FAQ I have gotten to a point where I am able to embed the CEF browser into a GtkWidget, but I am only able to make this work if I create the widget as a top-level GTK window like this:
m_widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Where m_widget is the main widget of the wxWebView I'm trying to create. The issue with this is that now the CEF browser is coming up in its own GTK window, but what I really need is for the CEF browser to fit into the wxBoxSizer that I am adding my wxWebViewChromium widget to. To get this working to this point I split out some of the code from the Create method into the OnSize method so that I could get the XID of the GtkWidget after the GtkWidget was realized, and create the CEF browser after this happened. The OnSize method is connected to wxEVT_SIZE. My code is now as follows.
Updated portion of Create Method
// Actual creation of CEF browser moved to OnSize function so we
// can guarantee the widgets have been realized
this->Bind(wxEVT_SIZE, &wxWebViewChromium::OnSize, this);
// Works but as a top-level GTK window only
m_widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
// None of these work
//m_widget = gtk_drawing_area_new();
//m_widget = gtk_vbox_new(false, 0);
g_object_ref( m_widget );
Updated portion of OnSize Method
if (!cef_browser_created)
{
cef_browser_created = true;
CefBrowserSettings browsersettings;
CefWindowInfo info;
XSetErrorHandler(XErrorHandlerImpl);
XSetIOErrorHandler(XIOErrorHandlerImpl);
gtk_widget_realize(m_widget);
::Window xwindow = GTK_WINDOW_XID(gtk_widget_get_window(m_widget));
DCHECK(xwindow);
GtkAllocation gtk_alloc;
gtk_widget_get_allocation(m_widget, &gtk_alloc);
CefRect cef_rect (
(int)gtk_alloc.x,
(int)gtk_alloc.y,
(int)gtk_alloc.width,
(int)gtk_alloc.height
);
info.SetAsChild(xwindow, cef_rect);
m_parent->DoAddChild( this );
PostCreation( size );
CefBrowserHost::CreateBrowserSync(
info,
static_cast<CefRefPtr<CefClient> >(m_clientHandler),
create_url.ToStdString(),
browsersettings,
NULL
);
}
When I try to make m_widget something other than a top-level GTK widget (as shown in the commented-out portions of the code for the Create method above), I get the following error at run-time.
Gtk: IA__gtk_widget_realize: assertion 'GTK_WIDGET_ANCHORED (widget) || GTK_IS_INVISIBLE (widget)' failed
Gdk: gdkdrawable-x11.c:952 drawable is not a pixmap or window
Check failed: xwindow.
How can I make this window appear inside my wxBoxSizer instead of as its own GTK top-level window?

CEF expects X11 window handle. GtkWidget handle was required in previous versions of CEF. The documentation on that wiki page seems to be outdated. Make sure the X11 handle is valid (show the window) before passing it to CEF.
I can only suggest to take a look at how GTK window is created in cefclient sample application:
https://bitbucket.org/chromiumembedded/cef/src/98f59f47fd395c170f389eba36f2ef2e06a500a8/tests/cefclient/browser/root_window_gtk.cc?at=master&fileviewer=file-view-default#root_window_gtk.cc-233
Also take a look at CreateBrowser() - it uses GetXWindowForWidget():
https://bitbucket.org/chromiumembedded/cef/src/98f59f47fd395c170f389eba36f2ef2e06a500a8/tests/cefclient/browser/browser_window_std_gtk.cc?at=master&fileviewer=file-view-default#browser_window_std_gtk.cc-91

Related

C++ Set window below (or above) the icons on the desktop

I'm trying to place a window either above or below the icons on the desktop. I mostly just want it to stay attached to the desktop at all times. Similar to Rainmeter or Wallpaper engine. So far, everything I tried either disables interaction, or gets minimized when you use the "Show Desktop" button. Any ideas on how to achieve this? I'm using electron and a native module in node to do this.
It's an old subject, but I'll find out how to do it recently and answer it.
The method is to find the handle of SHELLDLL_DefView, the parent of the desktop window, and then make the SHELLDLL_DefView handle the parent of my window to fix it to the desktop.
The method is to find the handle of SHELLDLL_DefView, the owner of the desktop window, and then make the SHELLDLL_DefView handle the owner of my window to fix it to the desktop.
SHELLDLL_DefView is located under the Progma or WorkerW handle. This is a code to prevent ShowDesktop from being used in the Electget package created by ffi-napi to attach the Electron browserWindow to the desktop.
const GWLP_HWNDPARENT = -8;
// find SHELLDLL_DefView in Progma
const progman = user32.FindWindowExA(ref.NULL, ref.NULL, 'Progman', ref.NULL);
let defView = user32.FindWindowExA(progman, ref.NULL, 'SHELLDLL_DefView', ref.NULL );
// find SHELLDLL_DefView in WorkerW
if (!defView) {
const desktopHWnd = user32.GetDesktopWindow();
let workerW = 0;
do {
workerW = user32.FindWindowExA(desktopHWnd, workerW, 'WorkerW', ref.NULL);
defView = user32.FindWindowExA(workerW, ref.NULL, 'SHELLDLL_DefView', ref.NULL );
} while (!defView && workerW);
}
if (!defView) return false;
// make the SHELLDLL_DefView handle the parent of my window
user32.SetWindowLongPtrA(hWnd, GWLP_HWNDPARENT, defView);
This allows you to create a window where you can click and interact without being hidden by ShowDesktop.
2022-03-29
There was a wrong word, so I corrected it. According to doc, it is not a parent window, but an owner window. In the doc, it is strange that the GWLP_HWNDPARENT constant is related to the parent window. However, when tested with Spy++, the corresponding constant changes the owner window.

CFileDialog freezes when launched from Modeless Dialog

I'm attempting to call CFileDialog to allow the user to select a file.
My program is a MFC application and I am using VS2005.
Here is my problem:
I have main View that creates a modeless dialog box. In this dialog box I have a menu item that makes a simple CFileDialog call:
CFileDialog dlgFile(true);
dlgFile.DoModal();
But the program always hangs on the DoModal. The program does not respond to commands and Task manager says it has stopped responding.
Other interesting pieces of information:
This has only been observed on Windows 8, Windows 7 machines seem to be unaffected.
I created a new project with the same basic View->modeless dialog->CFileDialog scheme and it worked just fine.
When I make a call to MessageBox, it appears behind the dialog box (I have to hit alt to get it up front), but the program is still responsive.
If I make the same CFileDialog call in the main View, it pops up without any problem.
Another annoying issue that may or may not be related to this: When the CFileDialog call is working (on Win 7), selecting "computer" in the browse window shows a blank white screen (Everything else works fine).
The closest questions I could find is this: Why does CFileDialog::DoModal() Hang?
However, I don't use threads (At least I'm 95% sure I don't, this isn't just my project). Is my project automatically being threaded? If so, how can I make sure that isn't causing me problems?
I've the same problem in VS2008.
Try to set to false the last parameter in CFileDialog constructor (bVistaStyle)
dlgFile.DoModal() calls run modal routine, it enables/disables and refocus windows, maybe that's the problem. You can try GetOpenFileName instead:
void CModeless::foo()
{
//EnableWindow(0);
OPENFILENAME ofn = { 0 };
char buf[300];
memset(buf, 0, 300);
ofn.lpstrFile = buf;
ofn.nMaxFile = 300;
ofn.lStructSize = sizeof(OPENFILENAME);
//ofn.hwndOwner = AfxGetApp()->m_pMainWnd->m_hWnd;
GetOpenFileName(&ofn);
//EnableWindow(1);
}
If there is no bug, then uncomment EnableWindow and ofn.hwndOwner, try again. You can also try this method:
void CModeless::foo()
{
CWnd *wnd = GetParent(); //or AfxGetApp()->m_pMainWnd
EnableWindow(0);
CFileDialog dlg(TRUE, 0, 0, 0, 0, wnd, 0, 1);
dlg.DoModal();
EnableWindow(1);
MSG msg;
while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
if (!AfxGetApp()->PumpMessage())
break;
SetFocus();
}
My CDialogEx based dialog was unable to show the CFileDialog. From within my CDocument, everything worked fine.
I found, that my dialog had a custom control, that was derived from CStatic and overrode the virtual WindowProc( ).
After commenting the WindowProc, the CFileDialog showed as usual.
The issue is only for the "m_bVistaStyle" CFileDialog, and the freeze occours in IFileDialog->Show().

CPropertySheet only shows for a second

I am trying to add a CPropertySheet with three CPropertyPages to my MFC application. My problem is that the Property sheet only shows for less than a second then closes. When I open a different modal dialog after creating the CPropertySheet, the CPropertySheet stays open and I can use it with no problems. Here is my code:
BOOL CSLIMOptCplusplusApp::InitInstance()
{
CWinApp::InitInstance();
SQLHENV m_1;
EnvGetHandle(m_1);
Login lgn;
lgn.DoModal();
CImageSheet* imagedlg = new CImageSheet("Image Capture Dialog" );
CImageDisplay* pageImageDisplay = new CImageDisplay;
CImageDimensions* pageImageDimensions = new CImageDimensions;
ListOption* pageListOption = new ListOption;
ASSERT( imagedlg );
ASSERT( pageImageDisplay );
ASSERT( pageImageDimensions );
ASSERT( pageListOption );
imagedlg->AddPage( pageListOption);
imagedlg->AddPage( pageImageDimensions );
imagedlg->AddPage( pageImageDisplay );
imagedlg->Create( NULL,
-1,
WS_EX_CONTROLPARENT | WS_EX_TOOLWINDOW );
imagedlg->ShowWindow( SW_SHOW );
I think my problem may be at imagedlg->Create( when I use NULL as the first parameter. The tutorial I was following used this in place of the NULL. However, that gives the error:
IntelliSense: argument of type "CSLIMOptCplusplusApp *" is incompatible with parameter of type "CWnd *"
I also tried imagedlg->Create(); and it also only flashes for a moment.
I would like my CPropertySheet to stay open until it is closed. Thanks for any help!
EDIT:
Here is an image of what I wish my property sheet to look like. My first tab used a ListControl to change database options, the other two tabs are going to do other things. My intent is to keep the dialog/propertysheet looking the same as it does now, but to stay open instead of closing.
Your problem lies in trying to construct a property sheet within a dialog based application. Actually, your choice of executing everything within InitInstance can be problematic at times.
For starters, there's no need to create all of your objects on the heap (ie. using 'new'). But, if that's what you want, ok. As for your original problem of the sheet only displaying for a moment, InitInstance is designed to return immediately if not told otherwise. Thus, you see the sheet for an instance. This is due to MFC expecting a valid pointer to the CWinApp class derived member variable called 'm_pMainWnd' (actually, CWinThread::m_pMainWnd). If you want to start a property sheet, or, main dialog from within InitInstance, you need to set that variable to a valid window. Here's a quick sample I wrote:
CPropertySheet* m_pdlgPropertySheet = new CPropertySheet(_T("Simple PropertySheet"));
ASSERT(m_pdlgPropertySheet);
// Add three pages to the CPropertySheet object. Both m_pstylePage,
// m_pcolorPage, and m_pshapePage are data members of type
// CPropertyPage-derived classes in CView-derived class.
Page1* m_pstylePage = new Page1;
m_pstylePage->Construct(IDD_DIALOG1);
Page2* m_pcolorPage = new Page2;
m_pcolorPage->Construct(IDD_DIALOG2);
m_pdlgPropertySheet->AddPage(m_pstylePage);
m_pdlgPropertySheet->AddPage(m_pcolorPage);
m_pMainWnd = m_pdlgPropertySheet;
INT_PTR nResponse = m_pdlgPropertySheet->DoModal();
Note the line above DoModal. If you need additional info, take a look at Creating a full application using the CPropertySheet. Lastly, you may want to read up on how MFC starts an application and what is expected.

How can I close only a GLUI window

I'm doing an uni project with initially one main GLUT window and will have some sort of "login" GLUI window that, if succeed Draws the glut window. However I want to be able to close the login window after it succeeds.
I've been struggling with this for quite some time, and I've only managed to get it working with glui->hide. However I'm pretty sure the process/thread whatever keeps running on background decreasing the program's performance.
I want to be able to have that similar behavior thats in hide() method, but instead of hiding , deleting/closing it.
I tried glui->close(), but it doesn't disappear. Also tried to glui->unlink() before closing and still doesn't do the desired behavior.
Is there any way I can close only the GLUI window without closing the whole program nor any GLUT window?
This is a bit of my main function:
glui = GLUI_Master.create_glui( "GLUI", 0, 400, 150 );
glui->add_statictext( "Login" );
glui->add_separator();
edittext = glui->add_edittext( "Username:", GLUI_EDITTEXT_TEXT);
edittext->set_w(200);
edittext1 = glui->add_edittext( "Password:", GLUI_EDITTEXT_TEXT);
edittext1->set_w(200);
glui->add_button( "OK", 303,validateLogIn );
glui->set_main_gfx_window( main_window );
GLUI_Master.set_glutIdleFunc( myGlutIdle );
If you remove GLUI_Master.set_glutIdleFunc( myGlutIdle ); then glui->close() should work.

ActiveX HWND, DirectX WindowLess mode

I would like to render video in ActiveX control (not in pop-up DirectShow window). I have:
IID_IVMRWindowlessControl
IID_IVMRFilterConfig9
CLSID_VideoMixingRenderer9
I would like to set WindowLess mode, but I don't know how to get HWND of..., exactly, of what? IEFrame, HTML element?
hr = pWc->SetVideoClippingWindow(???);
Anyone with some hint?
Regards.
First of all, add this to the constructor of your ActiveX control:
// this seemingly innocent line is _extremely_ important.
// This causes the window for the control to be created
// otherwise, you won't get an hWnd to render to!
m_bWindowOnly = true;
Your ActiveX control will have a member variable called m_hWnd that you'll be able to use as a render target. without the m_bWindowOnly variable set true, the ActiveX control won't create its own window.
Finally, pick your renderer (VMR9 for example)
CRect rcClient;
CComPtr<IBaseFilter> spRenderer;
CComPtr<IVMRWindowlessControl9> spWindowless;
// Get the client window size
::GetClientRect(m_hWnd, rcClient);
// Get the renderer filter
spRenderer.Attach( m_pGraph->GetVideoRenderer() );
if( ! spRenderer )
return E_POINTER;
spWindowless = spRenderer;
if( spWindowless )
{
spWindowless->SetVideoClippingWindow( m_hWnd );
spWindowless->SetVideoPosition(NULL, rcClient);
spWindowless.Release();
}
spRenderer.Detach();
Please note that my graph object is a custom object and that GetVideoRenderer() is one of my own functions - it returns an IBaseFilter*.
It took me ages to find this one out. ATL is poorly documented, which is a shame, because it's an excellent technology. Anyways, hope this helps!
freefallr's info is extremely helpful, but I don't think it answers your question completely. The trick with windowless activex controls is that you don't get a window. When you draw you'll just basically get a device context and you have to respond to call from the browser and only draw when it tells you to.
The interfaces required are here: http://msdn.microsoft.com/en-us/library/ms682300%28v=VS.85%29.aspx
more info here: http://msdn.microsoft.com/en-us/library/aa751970%28VS.85%29.aspx#OC96_and_Windowless_
We've been meaning to add support for this in FireBreath (http://firebreath.org) for awhile; we have support for it in all npapi browsers, but looks like we don't (yet) support IE. If you find more details, post a summary here =]