Recent

Author Topic: [SOLVED] TTimer fires more frequently when mouse is moving (?)  (Read 3152 times)

MMarie

  • New Member
  • *
  • Posts: 44
  • Right, lets bodge this pisspot
    • Homepage
Im currently working on a small (and quite crappy) applications that renderse in some scrolling text using a TTimer firing every 60th of a second. I've noticed that my text scrolls much smoother when I'm constantly moving my mouse in my window. Why does this happend and can I get the timer to update smoothly every time?

I know that for rendering I should probably use an extra thread that does it for me, but I just wanted a quick solution %) (and honestly at this point im quite intringuied as to why this happens).

Code:
Code: Pascal  [Select][+][-]
  1. unit uFrmMain;
  2.  
  3. { lazarus finnlines färjan ; a jumalauta fan demo }
  4.  
  5. {$mode objfpc}{$H+}
  6.  
  7. interface
  8.  
  9. uses
  10.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, LCLType, ExtCtrls, StdCtrls;
  11.  
  12. type
  13.  
  14.   { TForm1 }
  15.  
  16.   TForm1 = class(TForm)
  17.     render_timer: TTimer;
  18.     procedure FormCreate(Sender: TObject);
  19.     procedure FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
  20.     procedure FormShow(Sender: TObject);
  21.     procedure render_timerTimer(Sender: TObject);
  22.   private
  23.     scroll_text: String;
  24.     background: TPortableNetworkGraphic;
  25.     water: TPortableNetworkGraphic;
  26.     sun: TPortableNetworkGraphic;
  27.     boat: TPortableNetworkGraphic;
  28.  
  29.     first_frame: Boolean;
  30.     scroller_x: LongInt;
  31.   public
  32.   end;
  33.  
  34. var
  35.   Form1: TForm1;
  36.  
  37. implementation
  38.  
  39. {$R *.lfm}
  40. {$R resources.rc}
  41.  
  42. { TForm1 }
  43.  
  44. procedure TForm1.FormCreate(Sender: TObject);
  45. begin
  46.   self.render_timer.Interval:= 20;
  47.  
  48.   { ... cut out resource loading here ... }
  49.  
  50.   self.first_frame:=True;
  51. end;
  52.  
  53. procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
  54. begin
  55. {...}
  56. end;
  57.  
  58. procedure TForm1.FormShow(Sender: TObject);
  59. begin                                  
  60.   self.WindowState:=wsFullScreen;
  61.   self.render_timer.enabled:=True;
  62. end;
  63.  
  64. procedure TForm1.render_timerTimer(Sender: TObject);
  65. var
  66.   cvs: TBitmap;
  67. begin
  68.   cvs := TBitmap.Create;
  69.   cvs.SetSize(self.Canvas.Width, self.Canvas.Height);
  70.                
  71.   cvs.Canvas.Font.Size:=50;
  72.   cvs.Canvas.Font.Color:=clYellow;
  73.  
  74.   if (self.first_frame) then
  75.   begin
  76.     self.first_frame:=False;
  77.     self.scroller_x := round(cvs.Canvas.TextWidth(self.scroll_text) * 0.3);
  78.   end;
  79.  
  80.   cvs.Canvas.Brush.Color:=clBlack;
  81.   cvs.Canvas.FillRect(0, 0, cvs.Width, cvs.Height);
  82.  
  83.   cvs.Canvas.Draw(0, 0, self.background);
  84.   cvs.Canvas.Draw(self.Canvas.Width - self.sun.Width, 10, self.sun);
  85.   cvs.Canvas.Draw(0, self.Canvas.Height div 10, self.water);
  86.  
  87.   cvs.Canvas.TextOut(self.scroller_x, self.Canvas.Height div 2, self.scroll_text);
  88.   self.scroller_x := self.scroller_x - 5;
  89.  
  90.   self.Canvas.Draw(0, 0, cvs);
  91.   cvs.Free;
  92. end;
  93.  
  94. end.
  95.  
« Last Edit: April 07, 2024, 01:18:19 am by MMarie »
i use arch btw

alpine

  • Hero Member
  • *****
  • Posts: 1389
Re: TTimer fires more frequently when mouse is moving (?)
« Reply #1 on: April 05, 2024, 09:41:42 am »
Quote
Why does this happend and can I get the timer to update smoothly every time?

Timer messages are usually merged in one if they arrive too fast. By moving a mouse you're inserting mouse messages between and timer messages come in bigger numbers i.e. they can't be merged.

You're effectively bypassing the Paint mechanism of the widgetset. You must do the painting in Paint or OnPaint if provided. But paint messages are merged in the same way as the timer, so it will be even more erratic.

"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

wp

  • Hero Member
  • *****
  • Posts: 12865
Re: TTimer fires more frequently when mouse is moving (?)
« Reply #2 on: April 05, 2024, 10:20:20 am »
I don't know if my following code will result in smoother scrolling, but your painting code inside the timer event definitely is wrong, it will even crash the application in some non-windows operating systems because a valid canvas exists only inside the paint cycle.

You must put all the drawing code into the OnPaint event handler of the control which you want to display the scrolling text (according to your code, the form), and in the OnTimer event you must request that control to repaint itself (Invalidate):

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormPaint(Sender: TObject);  // --> OnPaint handler for Form1
  2. var
  3.   cvs: TBitmap;
  4. begin
  5.   cvs := TBitmap.Create;
  6.   cvs.SetSize(self.Width, self.Height);
  7.                
  8.   cvs.Canvas.Font.Size:=50;
  9.   cvs.Canvas.Font.Color:=clYellow;
  10.  
  11.   if (self.first_frame) then
  12.   begin
  13.     self.first_frame:=False;
  14.     self.scroller_x := round(cvs.Canvas.TextWidth(self.scroll_text) * 0.3);
  15.   end;
  16.  
  17.   cvs.Canvas.Brush.Color:=clBlack;
  18.   cvs.Canvas.FillRect(0, 0, cvs.Width, cvs.Height);
  19.  
  20.   cvs.Canvas.Draw(0, 0, self.background);
  21.   cvs.Canvas.Draw(self.Canvas.Width - self.sun.Width, 10, self.sun);
  22.   cvs.Canvas.Draw(0, self.Canvas.Height div 10, self.water);
  23.  
  24.   cvs.Canvas.TextOut(self.scroller_x, self.Canvas.Height div 2, self.scroll_text);
  25.   self.scroller_x := self.scroller_x - 5;
  26.  
  27.   self.Canvas.Draw(0, 0, cvs);
  28.   cvs.Free;
  29. end;
  30.  
  31. procedure TForm1.render_timerTimer(Sender: TObject);  // ---> OnTimer event handler of the timer
  32. begin
  33.   Invalidate;
  34. end;

TRon

  • Hero Member
  • *****
  • Posts: 4377
Re: TTimer fires more frequently when mouse is moving (?)
« Reply #3 on: April 05, 2024, 05:59:07 pm »
The most relevant bits have been said already but do realize that a timer is just an event and not some sort of interrupt that is guaranteed to fire when it is suppose to.

Besides that, whenever you do not finish your drawing within the amount of time you are suppose to do it (you have set the timer after all) then you 'skip' a beat which results in what we perceive as stuttering.

That is part of the game when using hardware that doesn't supply a proper syncing mechanism. Now feel free to burn me  :)
Today is tomorrow's yesterday.

MMarie

  • New Member
  • *
  • Posts: 44
  • Right, lets bodge this pisspot
    • Homepage
Re: TTimer fires more frequently when mouse is moving (?)
« Reply #4 on: April 07, 2024, 01:17:52 am »
I don't know if my following code will result in smoother scrolling, but your painting code inside the timer event definitely is wrong, it will even crash the application in some non-windows operating systems because a valid canvas exists only inside the paint cycle.

You must put all the drawing code into the OnPaint event handler of the control which you want to display the scrolling text (according to your code, the form), and in the OnTimer event you must request that control to repaint itself (Invalidate):

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormPaint(Sender: TObject);  // --> OnPaint handler for Form1
  2. var
  3.   cvs: TBitmap;
  4. begin
  5.   cvs := TBitmap.Create;
  6.   cvs.SetSize(self.Width, self.Height);
  7.                
  8.   cvs.Canvas.Font.Size:=50;
  9.   cvs.Canvas.Font.Color:=clYellow;
  10.  
  11.   if (self.first_frame) then
  12.   begin
  13.     self.first_frame:=False;
  14.     self.scroller_x := round(cvs.Canvas.TextWidth(self.scroll_text) * 0.3);
  15.   end;
  16.  
  17.   cvs.Canvas.Brush.Color:=clBlack;
  18.   cvs.Canvas.FillRect(0, 0, cvs.Width, cvs.Height);
  19.  
  20.   cvs.Canvas.Draw(0, 0, self.background);
  21.   cvs.Canvas.Draw(self.Canvas.Width - self.sun.Width, 10, self.sun);
  22.   cvs.Canvas.Draw(0, self.Canvas.Height div 10, self.water);
  23.  
  24.   cvs.Canvas.TextOut(self.scroller_x, self.Canvas.Height div 2, self.scroll_text);
  25.   self.scroller_x := self.scroller_x - 5;
  26.  
  27.   self.Canvas.Draw(0, 0, cvs);
  28.   cvs.Free;
  29. end;
  30.  
  31. procedure TForm1.render_timerTimer(Sender: TObject);  // ---> OnTimer event handler of the timer
  32. begin
  33.   Invalidate;
  34. end;

thank you all for your great help! Its been a while since I did worked with something like Delphi or Lazarus, I'll be reworking my project to follow your help.
i use arch btw

 

TinyPortal © 2005-2018