Recent

Author Topic: [SOLVED] TFontDialog unresponsive if application particularly busy in background  (Read 1214 times)

robert rozee

  • New Member
  • *
  • Posts: 35
hi,
    i've been having problems with the lazarus dialogs, in particular TFontDialog.

my application is quite big, a graphical terminal emulator that can have quite a bit going on in the background. when pushing things to the absolute limit, with both an animated graphics screen and a fast scrolling text screen (the graphics overlay the text, each on separate canvases), if i call up a FontDialog object the dialog appears but  does not respond to clicking either of the 'Cancel' or 'OK' buttons. the FontDialog does, however, usually close when the top-right-corner 'X'  button is clicked.

if i click on the 'OK' button, then wait for a while (10's of seconds to a couple of minutes) the dialog sometimes does eventually respond. if i reduce the background CPU load, thing work as normal.

my feeling is that the large quantity of visual activity going on while the FontDialog is displayed is impeding the LCL's handling of the 'OK' and 'Cancel' buttons. has anyone encountered similar issues before?

i have variously looked into the lazarus source code to find the source behind TFontDialog, but never managed to locate it. it seems that one possible solution (if it is simply the dialog being starved of CPU resources) would be to somehow hook the initial click events for the dialog's 'OK' and 'Cancel' buttons, and to there set a flag that briefly pauses other screen activity until the dialog has successfully closed.

i was also experiencing similar (bot not so severe) problems with TOpenDialog and TSaveDialog, but was able to track this down to a race-condition between two threads elsewhere in my application. with that fixed the problems with open and save went away, but the problem with font is not fully cured.


any help or suggestions much appreciated!


cheers,
rob   :-)
« Last Edit: September 05, 2021, 09:31:04 am by robert rozee »

jamie

  • Hero Member
  • *****
  • Posts: 4915
Re: TFontDialog unresponsive if application particularly busy in background
« Reply #1 on: September 03, 2021, 12:35:15 am »
Without knowing how you handle your key input processing it's hard to say however, you should detour your key inputs if you are somehow hooking into the system when there is a dialog box being displayed.

 TApplication has a property that indicates you are in dialog mode.
 Like ModalLevel, Also events etc..

The only true wisdom is knowing you know nothing

robert rozee

  • New Member
  • *
  • Posts: 35
Re: TFontDialog unresponsive if application particularly busy in background
« Reply #2 on: September 03, 2021, 01:35:46 am »
the FontDialog is called up from a right-click popup menu on the main form, and so my keyboard handlers are not involved. all visuals are driven by several timer routines (one at 15ms, one at 45ms, and two at 300ms) and these carry on happily when the FontDialog is open.

ModalLevel is interesting - i'll have a go at using it to throttle back the speed of animation while the FontDialog is open. although since the dialog operates correctly while it is open, misbehaving only when one goes to close it by clicking on 'Cancel' or 'Ok", what i really want is some way of looking into the dialog to see if 'Cancel' or 'OK' have been pressed. that way my application can respond by just briefly throttling back for the instance when the dialog is being starved of CPU resources.

my aplication does have a 256k serial buffer, but when running full-tilt this can quickly fill up. i did experiment with simply shutting down everything while the FontDialog was opened - this worked, but the serial buffer only provided enough storage for the 10 or 15 seconds.


cheers,
rob   :-)

robert rozee

  • New Member
  • *
  • Posts: 35
Re: TFontDialog unresponsive if application particularly busy in background
« Reply #3 on: September 04, 2021, 02:48:24 pm »
note for posterity:

i came up with the following 'solution' that works passably. basically i measure the time between successive calls to the 15ms timer event that i'm using for screen output, and count up the number of times an event is missed (due to the LCL being swamped). when the rate of missed events goes over a certain level, i skip a timer event deliberately. i also skip any 'catchup' events that the LCL seems to throw in if it is being swamped - these events just making the swamping worse anyway!

the end result is that the CPU load is reduced sufficiently to allow the LCL to (partially) recover, and for various dialogs to function correctly.

it would be preferable if the LCL had some mechanism for indicating it is being swamped, such as an accessible counter of the number of queued graphic actions - based upon the LCL behaviour, it does seem that things like moving data around on a canvas are queued. but i've not been able to find such a counter.


cheers,
rob   :-)


Code: Pascal  [Select][+][-]
  1. // returns how much time has elapsed since GetTickCount64 was assigned to a counter
  2. function timesince(counter:int64):int64;
  3. begin
  4.   result:=GetTickCount64-counter
  5. end;
  6.  
  7.  
  8.  
  9. procedure TForm1.Timer1Timer(Sender: TObject);                         // timer, 15ms interval
  10. const mark:int64=0;                                                    // time at (near) start of previous timer event
  11.        UDC:integer=0;                                                  // up/down counter, used for defining threshold for skipping
  12.  
  13. begin
  14. // ******** handle LCL swamping, which will result in both (a) missed timer events, and, (b) the LCL inserting 'catchup' events
  15.   if timesince(mark)<5  then exit;                                     // skip any 'catchup' events that the LCL throws at us
  16.   if timesince(mark)<20 then UDC:=max(0, UDC-1)                        // we are within a few ms of the set time interval (15ms)
  17.                         else UDC:=UDC+3;                               // we are substantially over  "   "   "      "     ( "  )
  18.   mark:=GetTickCount64;
  19.   if UDC>20 then
  20.   begin                                                                // have exceeded limit for allowed over-runs
  21.     UDC:=10;                                                           // 10 --> once kicked in, skips every 5th or so time through
  22.     exit                                                               // ... this is sufficient to allow dialogs to still function
  23.   end;
  24.  
  25.  
  26. //////////////////////////////////////////
  27. // rest of code that does screen output //
  28. //////////////////////////////////////////
  29.  
  30.  
  31. end;
  32.  

jamie

  • Hero Member
  • *****
  • Posts: 4915
Re: TFontDialog unresponsive if application particularly busy in background
« Reply #4 on: September 04, 2021, 03:39:18 pm »
on windows graphics gets put at the low end of importance when things get busy..
The only true wisdom is knowing you know nothing

robert rozee

  • New Member
  • *
  • Posts: 35
Re: TFontDialog unresponsive if application particularly busy in background
« Reply #5 on: September 05, 2021, 09:30:04 am »
i should have mentioned, i'm running linux. although i'd imagine windows would behave similarly under extreme load.

have discovered a slightly better/simpler solution using ApplicationPropertiesIdle, see below.


cheers,
rob   :-)


Code: Pascal  [Select][+][-]
  1. var idleTS:int64;                                                      // timestamp of last call to ApplicationPropertiesIdle
  2.  
  3. // calls to here come to a standstill when the LCL becomes swamped
  4. // WARNING: stops being called if a dialog pops up a question (ie, TSaveDialog asks if you want to overwrite an existing file)
  5. procedure TForm1.ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean);
  6. begin
  7.   idleTS:=GetTickCount64
  8. end;
  9.  
  10. // returns how much time has elapsed since GetTickCount64 was assigned to a counter
  11. function timesince(counter:int64):int64;
  12. begin
  13.   result:=GetTickCount64-counter
  14. end;
  15.  
  16.  
  17.  
  18. procedure TForm1.Timer1Timer(Sender: TObject);                         // timer, 15ms interval
  19. const tock:boolean=false;
  20. begin
  21.   tock:=not tock;
  22.   if (timesince(idleTS)>100) and tock then exit;                       // no idle events in last 100ms -> LCL is swamped, so skip every 2nd time through                              
  23.  
  24.  
  25. //////////////////////////////////////////
  26. // rest of code that does screen output //
  27. //////////////////////////////////////////
  28.  
  29.  
  30. end;
« Last Edit: September 05, 2021, 10:09:36 am by robert rozee »

 

TinyPortal © 2005-2018