I'm writing a reparenting window manager in XCB and C++:http://ix.io/3yNo
At the moment it works pretty well, but occasionally when I close a window, all the windows of that application close because the process exits with a BadWindow. For example if I have a couple xfce4-terminal windows open, all managed by one process,and I close one, occasionally the application will close and I will get a BadWindow (invalid window parameter) error (in the app, not my wm). The very interesting thing is that this is not reproducible but kind of rare, probably a race condition between reporting the error and closing the window due to X11's asynchoronous nature.I have no clue where to begin debugging this, any tips?I kind of suspect it might be something in the Unmap O
Your link contains almost 500 lines of code. I am not going to try to fully understand that. Instead, I'll just randomly guess.
auto window_manager::handle_unmap_notify(xcb_unmap_notify_event_t *ev) -> void {
if (unmap_ignore > 0) {
unmap_ignore--;
return;
}
client *cl = nullptr;
size_t idx = 0;
for (client &c : clients) {
if (c.window == ev->window) {
cl = &c;
break;
}
idx++;
}
if (not cl)
return;
xcb_destroy_window(conn, cl->frame);
clients.erase(clients.begin() + idx);
}
You are destroying windows that are not yours. When the owner of the window accesses it the next time, it will get a BadWindow error.
Instead, you should check a window's WM_PROTOCOLS property and check for WM_DELETE_WINDOW. If that is present, you are supposed to send a WM_DELETE_WINDOW message to the window. See ICCCM § 4.2.8.1: https://tronche.com/gui/x/icccm/sec-4.html#s-4.2.8.1
Related
I'm currently working on writing a reparenting window manager using C++ and the XCB library. I unmanage windows when I recieve an UnmapNotify event; in this case the code is very simple:
if (unmap_ignore > 0) {
unmap_ignore--;
return;
}
client *cl = nullptr;
size_t idx = 0;
for (client &c : clients) {
if (c.window == ev->window) {
cl = &c;
break;
}
idx++;
}
if (!cl)
return;
xcb_unmap_window(conn, cl->frame);
clients.erase(clients.begin() + idx);
However, this does not work for some clients that don't bother unmapping the window (usually happens when you force kill the process). Then instead a DestroyNotify is sent. However the ev->window field there (typeof ev = xcb_destroy_notify_event_t) is some value that isn't what I want... here are some example logs (also note each line is a new DestroyNotify event, I get it twice for some reason and each time the #window field is different but not correct):
Found a client window: 4194307 but this was destroyed: 4194305
Found a client window: 4194307 but this was destroyed: 4194313
I remember before I tried to write a window manager in Xlib and had this exact same issue. I must be missing something obvious though, but if I look at the code of other reparenting window managers like Awesome or Herbsluftwm they just use the window field and have no problems. What am I doing wrong?
(my full code is here: http://ix.io/3yDj)
One thing I suspect is that it is just giving me two children windows of the destroyed parent instead of sending the actual parent. no idea why, but it is the most logical thing I can come up with. I just want to be able to handle this so I can unmap the frame for things that don't send UnmapNotify events.
I also first thought that it may report DestroyNotify on the parent window instead of the client reparented one, so I tried checking the frame but to no avail:
Found a client window: 2097152 but this was destroyed: 4194305
Found a client window: 2097152 but this was destroyed: 4194313
I've been truly stumped on this so any help is appreciated, thanks!
Awesome for example does the exact same thing as I tried with ev->window:
static void
event_handle_destroynotify(xcb_destroy_notify_event_t *ev)
{
client_t *c;
if((c = client_getbywin(ev->window)))
client_unmanage(c, CLIENT_UNMANAGE_DESTROYED);
else
for(int i = 0; i < globalconf.embedded.len; i++)
if(globalconf.embedded.tab[i].win == ev->window)
{
xembed_window_array_take(&globalconf.embedded, i);
luaA_systray_invalidate();
}
}
I'm trying to use the Leadtools API version 21 for automatically scanning some documents and here is a sudo code of what I have done (it runs in a secondary thread and the unlock has been done in the main thread):
void CheckRetCode(int rc)
{
if (SUCCESS != rc)
{
L_TCHAR errMsg[1024];
memset(errMsg, 0, sizeof(errMsg));
L_GetFriendlyErrorMessage(rc, errMsg, 1024, L_FALSE);
throw TLeadException(errMsg, rc);
}
}
void OnThreadExecute(void)
{
HTWAINSESSION hSession = nullptr;
APPLICATIONDATA appData;
L_INT nRet;
L_TCHAR pszTwnSourceName[1024];
LTWAINSOURCE sInfo;
memset(&appData, 0, sizeof(APPLICATIONDATA));
appData.uStructSize = sizeof(APPLICATIONDATA);
appData.hWnd = hWnd;// hWnd is valid handle of my main window
appData.uLanguage = TWLG_ENGLISH_USA;
appData.uCountry = TWCY_USA;
wcscpy(appData.szManufacturerName, L"MyCompanyName");
wcscpy(appData.szAppProductFamily, L"MyProductName");
wcscpy(appData.szAppName, appData.szAppProductFamily);
wcscpy(appData.szVersionInfo, L"Version 0.1.0.1");
nRet = L_TwainInitSession2(&hSession, &appData, LTWAIN_INIT_MULTI_THREADED);
CheckRetCode(nRet);// the exception gets catched elsewhere but no error reported here
memset(pszTwnSourceName, 0, sizeof(pszTwnSourceName));
wcscpy(pszTwnSourceName, L"EPSON Artisan837/PX830"); // the name of the scanner is verifyed
sInfo.uStructSize = sizeof(LTWAINSOURCE);
sInfo.pszTwainSourceName = pszTwnSourceName;
CheckRetCode(L_TwainSelectSource(hSession, &sInfo)); // No error reported here
CheckRetCode(L_TwainStartCapsNeg(hSession)); // in here I get the return value -84 which is reported as "TWAIN DS or DSM reported error, app shouldn't (no need for your app to report the error)."
// the rest of the code but we cannot get there since above code reports error
}
Can anyone tell me what I'm doing wrong? Is there a step that I'm missing here?
EditThe function L_TwainSelectSource() make no effort to make sure the supplied source is valid and does not even return an error. As result, if you set the selected source to a garbage name, it will act as if it accepted it. From that point on if you try to Get/Set anything or try to acquire an image, every function returns -84.
Thank you
Sam
To test your code, I put the main window’s handle in a global variable:
globalhWnd = hWnd;
And modified your function to use that handle like this:
void OnThreadExecute(void *)
{
...
appData.hWnd = globalhWnd; // hWnd is valid handle of my main window
...
}
Then created a thread for it from the main program like this:
globalhWnd = hWnd;
_beginthread(OnThreadExecute, 0, 0);
I tried this with 5 different Twain sources: 2 virtual and 3 physical scanners (one of them an old Epson). All 5 drivers returned SUCCESS when calling L_TwainStartCapsNeg() from within the thread.
Two possibilities come to mind:
The problem might be caused by something else in your code other than the thread function.
Or the problem could be specific to your Twain driver.
To rule out the first possibility, I suggest creating a small test project that only creates a similar thread and does nothing else and trying it with different scanners. If it causes the same problem with all scanners, send that test project (not your full application) to support#leadtools.com and our support engineers with test it for you.
If the problem only happens with a specific Twain driver, try contacting the scanner’s vendor to see if they have an updated driver.
I'm writing a Game Engine just for practice but I still stuck with the first chellange. The Window Manager.
https://github.com/thebenius/SDL
I've created a GitHub Repo to show you the code but don't worry. Its not much. But I absolutely don't know where is my mistake.
In the Code I create three Windows and I Manage the input for SDL_QUIT to stop the game loop and for SDL_WINDOWEVENT_CLOSE to close the windows.
Everything works fine until the last window is closed. As far as I know, now the SDL_QUIT Event must be emitted by SDL. But the Gameloop goes on.
I think I maybe have a kind of memory leak and there is still a windows saved. But I checked the window stack (Window::windows hashmap) it is empty. And also the variables in main are cleared.
I also tried to additionally clear the window and renderer variable in the hash map
Window::~Window() {
// Delete Window and Renderer
SDL_DestroyRenderer(Window::windows[this->windowID]->renderer);
SDL_DestroyWindow(Window::windows[this->windowID]->window);
Window::windows[this->windowID]->renderer = nullptr;
Window::windows[this->windowID]->window = nullptr;
// Delete Window from map
Window::windows.erase(this->windowID);
// Delete Window and Renderer
SDL_DestroyRenderer(this->renderer);
SDL_DestroyWindow(this->window);
// Reset Pointer
this->renderer = nullptr;
this->window = nullptr;
Nothing worked.
I'm new in C++ and SDL. I hope you can help me out.
Thank you o11c,
Your answer was the riddles solution.
I just put SDL_Quit() out of the Destructor. This obviously blocked the Event Handler to catch SDL_QUIT. So I put it to the constructor in atexit()
After that (don't know why before not) I got an Segfault when deleting the window pointer in main. I deleted that and just set them all to nullptr.
Now the WindowManager works properly. Thank you for your help
I think SDL_QUIT is only an hook called if you call SDL_Quit(), to give the user the opportunity to do some quit stuff, the manual:
You should call this function even if you have already shutdown each initialized subsystem with SDL_QuitSubSystem(). It is safe to call this function even in the case of errors in initialization
You can use this function with atexit() to ensure that it is run when your application is shutdown, but it is not wise to do this from a library or other dynamically loaded code
To catch a window close event see SDL_WindowEvent, SDL_WINDOWEVENT and SDL_WINDOWEVENT_CLOSE, the id of the closing window is given as argument.
* \file SDL_quit.h
*
* An ::SDL_QUIT event is generated when the user tries to close the application
* window. If it is ignored or filtered out, the window will remain open.
* If it is not ignored or filtered, it is queued normally and the window
* is allowed to close. When the window is closed, screen updates will
* complete, but have no effect.
*
* SDL_Init() installs signal handlers for SIGINT (keyboard interrupt)
* and SIGTERM (system termination request), if handlers do not already
* exist, that generate ::SDL_QUIT events as well. There is no way
* to determine the cause of an ::SDL_QUIT event, but setting a signal
* handler in your application will override the default generation of
* quit events for that signal.
*
* \sa SDL_Quit()
SDL_EventType#SDL_QUIT
An SDL_QUIT event is generated when the user clicks on the close button of the last existing window.
You shouldn't call SDL_Quit() in the destructor, but only once before leaving the application (is suggested to use it with atexit())
--- a/main.cpp
+++ b/main.cpp
## -32,17 +32,18 ## int main() {
}
-
// Delete Windows
- delete window;
- delete window2;
- delete window3;
+ // delete window;
+ // delete window2;
+ // delete window3;
// reset pointer
window = nullptr;
window2 = nullptr;
window3 = nullptr;
+ SDL_Quit();
+
// Close Program properly
return 0;
}
--- a/video/window.cpp
+++ b/video/window.cpp
## -51,7 +51,7 ## Window::~Window() {
// Shutdown if last window closed
if(this->windows.empty()) {
// Shutdown Video System
- SDL_Quit();
+ // SDL_Quit();
std::cout << "shuted down Video" << std::endl;
}
}
When I enter a wxDialog and I focus one wxTextCtrl nothing bad happens but as soon as I modify something in that text, like deleting one character, the application crashes with this message:
"debug assertion failed: map/set iterator not dereferencable"
The last method I found out is called before crashing is this one:
bool wxWindowMSW::HandleKillFocus(WXHWND hwnd)
{
#if wxUSE_CARET
// Deal with caret
if ( m_caret )
{
m_caret->OnKillFocus();
}
#endif // wxUSE_CARET
#if wxUSE_TEXTCTRL
// If it's a wxTextCtrl don't send the event as it will be done
// after the control gets to process it.
wxTextCtrl *ctrl = wxDynamicCastThis(wxTextCtrl);
if ( ctrl )
{
return false;
}
#endif
// Don't send the event when in the process of being deleted. This can
// only cause problems if the event handler tries to access the object.
if ( m_isBeingDeleted )
{
return false;
}
wxFocusEvent event(wxEVT_KILL_FOCUS, m_windowId);
event.SetEventObject(this);
// wxFindWinFromHandle() may return NULL, it is ok
event.SetWindow(wxFindWinFromHandle(hwnd));
return GetEventHandler()->ProcessEvent(event);
}
While running the application, this method is called many times; ctrl having a value of 0x00000000 all the time and therefor not returning false in the first IF clause.
While being inside the dialog and modifying the text, the value of ctrl changes to a real value 0x031194b0; then it enters the IF clause, returns false, and crashes.
The problem came from another code modification that I still do not find how it could have this effect. Internal behaviour of wxWidgets library maybe?
I'm really having trouble closing my console application with FreeGLUT.
I would like to know what the best way is to take every possible closing, because I don't want any memory leaks (I'm pretty afraid of those).
So I already tried the following, which is giving me an exception like this:
First-chance exception at 0x754e6a6f in myProject.exe: 0x40010005: Control-C.
int main(int argc, char **argv)
{
if( SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, true) )
{
// more code here as well ....
glutCloseFunc(close); // set the window closing function of opengl
glutMainLoop();
close(); // close function if coming here somehow
}
else
{
return 1;
}
return 0;
}
void close()
{
// keyboardManager is a pointer to a class
// which I want to delete, so no memory will leak.
if(keyboardManager) // do I need this check?
delete keyboardManager;
}
bool CtrlHandler(DWORD fdwCtrlType)
{
switch(fdwCtrlType)
{
// Handle the CTRL-C signal.
case CTRL_C_EVENT:
// and the close button
case CTRL_CLOSE_EVENT:
close();
return true;
// Pass other signals to the next handler.
case CTRL_BREAK_EVENT:
return false;
// delete the pointer anyway
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
default:
close();
return false;
}
}
So what goes right is:
Closing the window of glut
Closing the console application with the x
Closing my window of glut with my keyboardmanager if(keyboardManager->isKeyDown[27]) glutExit();
What goes wrong is:
Closing the console application with CTRL+C, it gives the exception from above.
This is in Visual Studio 2008 C++.
UPDATE
I found that the exception is thrown, because I'm in debug. So that won't be a problem. But the question is still open: What is the most elegant way to actually close glut?
atexit() seems to work as well, so maybe I can use this?
I use this function:
void glutLeaveMainLoop ( void );
There is more information on their sourceforge page but I never used that functionality:
The glutLeaveMainLoop function causes freeglut to stop the event loop. If the GLUT_ACTION_ON_WINDOW_CLOSE option has been set to GLUT_ACTION_CONTINUE_EXECUTION, control will return to the function which called glutMainLoop; otherwise the application will exit.
http://freeglut.sourceforge.net/docs/api.php#EventProcessing
It is safe to use delete on a null pointer, no need to check.
Thanks to Maarten's post, this works to me:
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE,GLUT_ACTION_CONTINUE_EXECUTION);
whenever you want to leave the mainloop without termiante the application use:
glutLeaveMainLoop();
don't forget to include "freeglut.h"
I use glutDestroyWindow(int handle);
or
According to ID: RigidBody at OpenGL forum
void destroy_window()
{
window_valid = -1;
}
void display_func()
{
if(window_valid == -1)
return;
// draw things
}
Try this method:
glutDestroyWindow(glutGetWindow());