How to use the graphics library in a stable way

I am new to this forum, so am not sure whether this is the right place to ask these questions. I tried to search the forum, but I could not find a similar question before. Be patient if this is the wrong place to ask these kind of questions, or if it has been asked before.

I am trying to develop a user interface in ROOT for some embedded processors. These processors communicate with network sockets to the ROOT program. The ROOT program uses the graphics library to present to the user some windows with frames in which buttons, check boxes, text entries, etc. are used. Up to now I did not succeed to make a stable program out of it. The idea is to create a TGMdiMainFrame for each embedded processor and within it a TGMdiFrame for each thread in the related embedded processor.
I run ROOT 5.21 on a Linux Lx86_64 Suse 10 platform with a dual processor core. I login with PuTTY from a Windows XP system with Xwin32 7.1 as Xwindows server. PuTTY uses SSH with a tunnel for the X server.
I have several problems, which I will present here one by one.

The main problem I have now is demonstrated in the attached demo source code.
I start root and I compile the code with:
.L TestGraphics.cpp+
then I create an object of the class in this code with:
TestEnv::TestGraphics TestG;
This creates the graphics I expect, but when I move the mouse over the green area, segmentation violations are generated repeatedly in TGContainer::HandleMotion ().

The first question is: Is this my fault, or is there a bug in the graphics library.
If I am doing something wrong, what should I do to get the same result without the segmentation violations?
TextGraphics.cpp (2.71 KB)

Hi,

How did you manage to compile this macro? I get:

Then, where is you TGMainFrame? You don’t create any…
I would suggest you to take a look at $ROOTSYS/tutorials/gui/mditest.C macro to see how to properly create a MDI application (it’s the primary goal of this macro)…
Anyway, with the few mods below, your code works for me (at least on Windows):

[code] class TestGraphics {
private:
TGMainFrame *fMain;
TGMdiMainFrame * MainFrame;
TGMdiFrame * MdiFrame;
TGCanvas * Canvas;
TGContainer * Container;
public:
TestGraphics ()
: MainFrame(0), MdiFrame (0), Canvas (0) {
fMain = new TGMainFrame(gClient->GetRoot (), InitialMainWidth, InitialMainHeight);
MainFrame = new TGMdiMainFrame(fMain, 0, InitialMainWidth, InitialMainHeight);
fMain->AddFrame(MainFrame, new TGLayoutHints(kLHintsExpandX | kLHintsExpandY));

  [...] // your code here

  fMain->MapSubwindows();
  fMain->MapWindow();
  fMain->Layout();

[/code]
Cheers,
Bertrand.

As I said, I use (from the root prompt)
.L TextGraphics.cpp+
which compiles without even a warning.
Chapter 25, page 416 of the documentation suggest to set the fMapSubwindows data member to kTRUE to activate the mapping of sub-windows. So, I just followed this suggestion and it seemed to work.
If it is a private member, I don’t understand this suggestion.

Do I need one? I could not find that in the documentation.
I could do already a lot without a TGMainFrame.
I added buttons and Text Entries to the TGMdiFrames and it worked.
The problem started when I tried to add a TGCanvas object to the TGMdiFrame.
In fact, I could find very little documentation for TGMdiFrame and TGMdiMainFrame. If I search for the TGMdiFrame I found only the description: “This file contains the TGMdiFrame class”, similar for TGMdiMainFrame. So I had to guess what these classes do. I could not even find where MDI stands for. I had the impression that TGMdiMainframe could be used instead of a TGMainFrame in order to add TGMdiFrames with their own windows manager.
Is there a description of these classes somewhere, I mean, not a list of members and methods, but a description of the purpose and functionality?

Thanks for the hint.
I’ll try to see if that solves my problem.

[quote=“F.Zwarts”][quote=“bellenot”]
Thanks for the hint.
I’ll try to see if that solves my problem.
[/quote][/quote]

I tried adding a TGMainFrame and it helps. I do not see the segmentation violations anymore. Thanks for the suggestion.

The next question is the AddFrame method of the TGMdiMainFrame, which is commented out now in the demo code. Is it true that this call is not needed? I am a bit confused because I do not really see the need in other cases either.
The creation of an object already had the root window in the first parameter of its constructor. Why is the AddFrame needed in some cases in addtion to referencing the root window in the constructor and why does the AddFrame produce an error in this case of TGMdiMainFrame?

Hi,

AddFrame is mandatory when adding frame in TGCompositeFrames (and inherited classes), as they have a list of children frames, together with their TGLayoutManagers. TGMDIMainFrame is a special case, and doesn’t have layout managers (by definition, as mdi children are free and can be moved/resized independently).
And please take a look at the attached code (your code - slightly modified), it is the proper way to implement MDI application.

Cheers, Bertrand.
TestGraphics.cpp (2.67 KB)

[quote=“bellenot”]Hi,

AddFrame is mandatory when adding frame in TGCompositeFrames (and inherited classes), as they have a list of children frames, together with their TGLayoutManagers. TGMDIMainFrame is a special case, and doesn’t have layout managers (by definition, as mdi children are free and can be moved/resized independently).
[/quote]

OK, thanks for this explanation. Is this documented somewhere?

Thanks for your suggestions.
I have a few questions about it.

  1. Why is fMain created with new? I always try to avoid new in order to reduce the chance for leaks. In fact, I see that new is used a lot in the examples. I wonder why. I also find it difficult in each case to see who is responsible for the deletion. There are many examples which show how things are constructed, but almost none which show how things are desctructed. I see that you added a new for fMain, but no delete for fMain (nor for fMainFrame. Isn’t this causing a memory leak?
  2. Is the menu bar required? Why?
  3. I don’t want to terminate the application in this class. So, why the call to gApplication->Terminate()? Remember that each embedded processor has one of these fMain frames on the screen. Exiting one of them should not terminate all others.
  4. You removed the redraw function. What is needed to make a change in one of the components in the container of the canvas of one of the MdiFrames visible on the screen? I found that changes are not always seen on teh screen immediately. I understand that it would be inefficient to update the screen for every change, because often a change is followed by another change. But what should be done to make all updates visible on the screen?

Hi,

Here is the documentation of the GUI:
ftp://root.cern.ch/root/doc/25WritingGUI.pdf
root.cern.ch/root/html/TGComposi … e:AddFrame
But I agree this specific topic is maybe not clear enough…

It is usually better to create objects on the heap than on the stack. The stack is small and you may have stack overflow by using it too much…
And I don’t delete fMain because I exit the application anyway. But I agree, I should delete fMain…

Just try to maximize and then restore your MDI child, you will see the reason :wink:

OK, fine. You don’t have to. It was just an example.

Usually MapSubwidows() and Layout() calls are sufficient.

Bertrand.

Thanks, this helps me a lot.

Then I have some final questions.
As you can imagin, this application is highly asynchronous. It uses many threads in order to react on external events. Sometimes an embedded processor needs attention, which needs changes in one or more frames, sometimes the user performs some actions, which also needs changes in the graphics.
I have the impression that the graphics library is not thread safe (because it uses CINT, which is not thread safe). If two threads try to change things simultaneuosly, I often see errors reported.
In addition to users, it seems that there are timers that perform graphic actions in a way that I cannot control.
E.g., I have a Close button in a frame. If the frame is deleted immediately after the user pressed this button, memory corruption occurs.

One question is how to wait for the time that a frame can be deleted safely. I mean really deleted, to avoid memory leaks.

The more general question is how to synchronize these calls to the graphics library. I use already my own global mutex to serialize the different threads when they call functions of the graphics library, but this does protect against simultaneous calls from a thread at the one hand and timers, or user actions at the other hand.
Is there a global mutex, used also by the internals of the graphics library, that I can use to serialize graphics actions?

Hi,

There are a few examples using threads in $ROOTSYS/test and in $ROOTSYS/tutorials/thread. ROOT has its own thread classes (TThread, TMutex, …)
You may also take a look at R__LOCKGUARD macros, used on global mutexes, and defined in TVirtualMutex.h
If it is not sufficent, I would need a piece of code showing what you want to achieve, and showing what is the problem, in order to be able to help.

Cheers, Bertrand.

The code I have is too large to show here. I mentioned already the example of closing a frame. Maybe it helps if I describe another similar example.
I have one frame with a button. If the user clicks the button a new thread is started, which creates a new frame with other components.
It turns out that if this new thread starts immediately to draw things on the screen, then memory corruption occurs. I assume that this happens because at the same time the first thread is still manipulating the button click (e.g. drawing it in pressed state and drawing it again in the normal state). I worked around it by inserting a delay in the startup of the new thread, but this slows down the application. In addition it is not clear what is the amount of time to delay the startup of the new thread in the worst case scenario, e.g. on a very busy processor.
(I noticed that in the DeleteWindow method of TGFrame, you also use a timer, which I think is there for similar reasons. Is this guaranteed to work, even in a worst case scenario of a very busy system?)
I would prefer (instead of a delay) an event (e.g. a mutex unlock or a condition signal) to wait for, which under good circumstances improves the response time and in bad circumstances does not crash the system. This means that this event must be generated by the internal functions of the graphics library. Is such a synchronization with e.g. the completion of a button click possible? I noticed that it is not enough if I use a mutex in the method connected (with the connect method) to the button click.

Well, I don’t really understand why the creation of a new thread would create problem, unless the new thread access an element that may have been modified by another thread…
And what do you mean by memory corruption? Does it crashes? Do you have the back trace?
Thread lock mechanism makes sense only to protect access to common data (e.g. globals) is it your case?
It would really help if you can prepare a short macro reproducing the problem. I can obviously try to reproduce the problem myself, but only if it is not urgent for you…
– Bertrand.

OK, I understand. Maybe I can construct a simple demo. If not, I keep the delays.

I now discovered that you also replaced TGContainer with TGCompositeFrame. Why is that? Is there something wrong with TGContainer? I have no problems using it.

Well, I have to check, but I had some performance and behaviour issues with TGContainer… And I always use TGCompositeFrame.
Anyway, I’ll check.
– Bertrand.

[quote=“bellenot”]Well, I have to check, but I had some performance and behaviour issues with TGContainer… And I always use TGCompositeFrame.
Anyway, I’ll check.
[/quote]
I did some experiments and I found indeed a problem with TGContainer.
If I add a button to the container, it is properly displayed, but clicking on it does not produce an action.
If the TGContainer is replaced with a TGCompositeFrame, as in your modifications, then the button is properly displayed and clicking on it initiates the right action.
So, I will switch to TGCompositeFrame as well.
This might be something to fix, though.

[quote=“F.Zwarts”]OK, I understand. Maybe I can construct a simple demo. If not, I keep the delays.
[/quote]

Well, the attached file does not really use threads, but it demonstrates the problems I see in a multithreaded environment. I used the SingleShot to simulate a different thread. The program shows a frame with a next button. Each time a next button is clicked, another similar frame is created. If the number of frames is larger than MaxNrFrames than all frames are deleted before the new one is created. In my case the program works if MaxNrFrames equals 1 or 2. If it is larger than segmentation violations occur. I see the following messages if MaxNrFrames equals 3. If it is larger similar errors are reported in addition.

Error in : BadWindow (invalid Window parameter) (TGMdiVerticalWinResizer XID: 8388799, XREQ: 61)
TGMdiVerticalWinResizer: 8388799

*** Break *** segmentation violation
Using host libthread_db library “/lib64/libthread_db.so.1”.
Attaching to program: /proc/6708/exe, process 6708
[Thread debugging using libthread_db enabled]
[New Thread 47193446155056 (LWP 6708)]
0x00002aec146977c5 in waitpid () from /lib64/libc.so.6
#1 0x00002aec14640b91 in do_system () from /lib64/libc.so.6
#2 0x00002aec12a597e3 in TUnixSystem::StackTrace ()
from /usr/local/kvi/lib/root5.21/lib/libCore.so
#3 0x00002aec12a5807a in TUnixSystem::DispatchSignals ()
from /usr/local/kvi/lib/root5.21/lib/libCore.so
#4
#5 0x0000005f00000137 in ?? ()
#6 0x00002aec160af7ef in TGWindow::Print ()
from /usr/local/kvi/lib/root5.21/lib/libGui.so
#7 0x00002aec16011289 in TGFrame::Print ()
from /usr/local/kvi/lib/root5.21/lib/libGui.so
#8 0x00002aec1668ecbe in RootX11ErrorHandler ()
from /usr/local/kvi/lib/root5.21/lib/libGX11.so
#9 0x00002aec16a403af in _XError () from /usr/X11R6/lib64/libX11.so.6
#10 0x00002aec16a42f42 in _XEventsQueued () from /usr/X11R6/lib64/libX11.so.6
#11 0x00002aec16a3349d in XPending () from /usr/X11R6/lib64/libX11.so.6
#12 0x00002aec15fd6986 in TGClient::ProcessOneEvent ()
from /usr/local/kvi/lib/root5.21/lib/libGui.so
#13 0x00002aec15fd6a6d in TGClient::HandleInput ()
from /usr/local/kvi/lib/root5.21/lib/libGui.so
#14 0x00002aec15fd6a8d in TGInputHandler::Notify ()
from /usr/local/kvi/lib/root5.21/lib/libGui.so
#15 0x00002aec12a58698 in TUnixSystem::DispatchOneEvent ()
from /usr/local/kvi/lib/root5.21/lib/libCore.so
#16 0x00002aec129e4106 in TSystem::InnerLoop ()
from /usr/local/kvi/lib/root5.21/lib/libCore.so
#17 0x00002aec129e7131 in TSystem::Run ()
from /usr/local/kvi/lib/root5.21/lib/libCore.so
#18 0x00002aec1299039f in TApplication::Run ()
from /usr/local/kvi/lib/root5.21/lib/libCore.so
#19 0x00002aec13e4f6e0 in TRint::Run ()
from /usr/local/kvi/lib/root5.21/lib/libRint.so
#20 0x000000000040109d in main ()
The program is running. Quit anyway (and detach it)? (y or n) [answered Y; input not from terminal]
Detaching from program: /proc/6708/exe, process 6708

Then the program hangs and does not respond to ^C or ^Z. I have to kill the program from a different terminal.
TextGraphics.cpp (3.74 KB)

OK, then, if I understand correctly, the attached example works (at least it works fine on Windows), but not your application with threads, right?
Then sorry, but I cannot tell anything useful with this code. I’ll try to make a working example as soon as I find some time.
– Bertrand.

[quote=“bellenot”]OK, then, if I understand correctly, the attached example works (at least it works fine on Windows), but not your application with threads, right?
Then sorry, but I cannot tell anything useful with this code. I’ll try to make a working example as soon as I find some time.
[/quote]
The attached example does not work on my Linux system, except if I lower the MaxNrFrames constant. Maybe for your Windows system the MaxNrFrames constant must be increased to demonstrate the problem. I don’t know. I don’t use Windows for ROOT.

Oh! OK, then I’ll try on Linux asap and I’ll let you know (as it works on Windows, independently of the number of chidren).
– Bertrand.

Hi,

Please try to replace:

for (size_t It = 0; It < MdiFrames.size (); ++It) { MdiFrames[It]->Cleanup (); MdiFrames[It]->DeleteWindow (); }by:for (size_t It = 0; It < MdiFrames.size (); ++It) { delete MdiFrames[It]; }
Since everything is done in the TGMdiFrame destructor:

[code]//______________________________________________________________________________
TGMdiFrame::~TGMdiFrame()
{
// TGMdiFrame destructor.

Cleanup();
fMain->RemoveMdiFrame(this);
}
[/code]
Cheers,
Bertrand.