Recent

Author Topic: trying to create an own component but getting SIGSEGV  (Read 6297 times)

r.lukasiak

  • Full Member
  • ***
  • Posts: 138
Re: trying to create an own component but getting SIGSEGV
« Reply #30 on: September 07, 2022, 12:43:12 am »
it wasn't public... now I can
Code: Pascal  [Select][+][-]
  1. Game.sack[Length(Game.sack)-1].OnMouseMove:=@Game.sack[Length(Game.sack)-1].MouseMove;
  2.  
without any issues but I actually prefer the other way, MouseMove assigned right in constructor.

Anyway after making it public, I was still getting SIGSEGV. I undid all changes related to the class, events etc and it didn't stop giving me SIGSEGV so I went further, undoing all I did today and I got to Data Module and reminded that i had the same problem years ago. Even though it was a different machine, different system, different Lazarus and FPC version. For some reason, dropping SQL components on Data Module is giving me SIGSEGV, and what's worse, it won't happen immediately. It will happen at random moment, after adding some code, not even related to the SQL components. Exactly as it happened today, first I created Data Module, then dropped the components onto it. A that moment everything was fine, it built without any problem. Next, I started moving classes to the Data Module unit, still everything fine, building several times, checking if everything is ok. after that, I just kept working with other procedures and then suddenly SIGSEGV. How is that even possible?
I moved the SQL components back to the main form, I actually only need TImageList and some classes to be shared, SQL components are doing fine on the mainform, now everything is working fine but it just beats me why it happens.

rvk

  • Hero Member
  • *****
  • Posts: 6111
Re: trying to create an own component but getting SIGSEGV
« Reply #31 on: September 07, 2022, 10:44:48 am »
it wasn't public... now I can
Code: Pascal  [Select][+][-]
  1. Game.sack[Length(Game.sack)-1].OnMouseMove:=@Game.sack[Length(Game.sack)-1].MouseMove;
  2.  
without any issues but I actually prefer the other way, MouseMove assigned right in constructor.
Actually, for a component, I would prefer to remove the OnMouseMove entirely and implement my own MouseMove.

If you look into TControl there is a MouseMove in protected.
For your own component you can put this in protected:
Code: Pascal  [Select][+][-]
  1. procedure TMyComponent.MouseMove(Shift: TShiftState; X, Y: Integer); override;

In that case you don't even have to assign the OnMouseMove and you can remove the OnMouseMove from the exposed events for your own component.
(unless you need to override the MouseMove in your actual program, not the component)
That's is the correct way of doing it for an actual component.

Anyway after making it public, I was still getting SIGSEGV. I undid all changes related to the class, events etc and it didn't stop giving me SIGSEGV ...
If you debug things, you should be able to pinpoint the exact line on which the SIGSEGV occurs.
You could share that procedure or function to see if you did something wrong.
Without any code it's just guessing for us what went wrong.

But now it works so that's for next time  ;)

r.lukasiak

  • Full Member
  • ***
  • Posts: 138
Re: trying to create an own component but getting SIGSEGV
« Reply #32 on: September 07, 2022, 05:48:46 pm »
Quote
Actually, for a component, I would prefer to remove the OnMouseMove entirely and implement my own MouseMove.
That was actually my first approach but I couldn't figure out if it's in protected, public or private. I dropped a TImage on the form and peeked on how it's done and I got
Code: Pascal  [Select][+][-]
  1. procedure Tsq.P1panelMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
but I was always getting "no such a class member" or something like this.
Since I couldn't figure this out, I tried to assign the MouseMove even from outside the class, and then inside the constructor, but your solution is way better. I've already implemented it and it's working perfectly.

Just for learning purposes and better understanding, why is it different?
If I use OnMouseEvent on desing time, it's
Code: Pascal  [Select][+][-]
  1. MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
and your solution is
Code: Pascal  [Select][+][-]
  1. MouseMove(Shift: TShiftState; X, Y: Integer);
I mean the number of parameters.

Quote
If you debug things, you should be able to pinpoint the exact line on which the SIGSEGV occurs.
How could I do it? I tried to set a breakpoint on MainForm.Create and go step by step but SIGSEGV happens before that.

Quote
But now it works so that's for next time 
yes, so far everything works as intended. You've been really helpful, thank you!


rvk

  • Hero Member
  • *****
  • Posts: 6111
Re: trying to create an own component but getting SIGSEGV
« Reply #33 on: September 08, 2022, 12:43:11 am »
Just for learning purposes and better understanding, why is it different?
If I use OnMouseEvent on desing time, it's
Code: Pascal  [Select][+][-]
  1. MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
and your solution is
Code: Pascal  [Select][+][-]
  1. MouseMove(Shift: TShiftState; X, Y: Integer);
I mean the number of parameters.
The one with sender is an OnSomethingEvent. It's called by an action and therefore has a Sender parameter. You can assign your own events to it (like you normally do with OnSomething).

The one without Sender parameter is the internal procedure which normally calls the OnSomethingEvent.

Normally internally, for an event, there is a procedure Something which looks something like this.

Code: Pascal  [Select][+][-]
  1. procedure TMyClass.Something();
  2. begin
  3.   if assigned(FOnSomething) then FOnSomething();
  4. end;
And internally this is called when Something happens and that procedure calls the OnSomething the programmer assigned.

If you do it like that it's easy for a programmer inheriting the class to override the Something without the need to assign a OnSomething.
(And that's what you are doing now  ;) )

So... MouseMove is the internal procedure which calls OnMouseMove.

(And because you don't need Sender inside the class itself, because you already know where it came from, MouseMove doesn't have that parameter)
Now, because you don't have any interest in ever letting a programmer assign their own custom OnMouseMove, it's best to directly override the basis of MouseMove procedure itself.

Quote
If you debug things, you should be able to pinpoint the exact line on which the SIGSEGV occurs.
How could I do it? I tried to set a breakpoint on MainForm.Create and go step by step but SIGSEGV happens before that.
If an error occurs in your component, it's best to test this by creating an instance in code (for example a empty testform and create it in the formcreate) and putting a breakpoint on the creation line and stepping into it during debugging.

Because with components on a form are created before the formcreate it's difficult to debug. But if it's not on the form and you create it (as test) in code, it's easier to follow.
« Last Edit: September 08, 2022, 12:46:39 am by rvk »

r.lukasiak

  • Full Member
  • ***
  • Posts: 138
Re: trying to create an own component but getting SIGSEGV
« Reply #34 on: February 03, 2024, 11:11:40 pm »
hi everyone, again :-)

I'm sorry for reviving such an old post but I'm again facing some issues that were actually solved in this post.
I'm creating a class inheriting from TCustomControl (I also tried TPanel, even though it has a lot of stuff I don't need) and I'm trying to assign the OnMouseWheelUp/Down/Let/Right event with rather miserable result.

Code: Pascal  [Select][+][-]
  1. .
  2. .
  3. TMyScrollPanel = class (TKScrollPanel)
  4.     private
  5.       procedure WheelDown;
  6.     protected
  7.       procedure Click; override;
  8.       procedure Paint; override;
  9.     public
  10.       constructor Create(TheOwner: TComponent); override;
  11.  end;
  12.  
  13.  
  14. var
  15.   cib: Tcib;
  16.   MyPanel: TMyScrollPanel;
  17.  
  18. implementation
  19.  
  20. {$R *.lfm}
  21.  
  22. { Tcib }
  23. constructor TMyScrollPanel.Create(TheOwner: TComponent);
  24. begin
  25.   inherited;
  26.   OnMouseWheelDown:=@WheelDown;
  27. end;
  28.  
  29. procedure TMyScrollPanel.Click;
  30. begin
  31.   writeln('Click');
  32. end;
  33.  
  34. procedure TMyScrollPanel.Paint;
  35. begin
  36.   Canvas.Brush.Color:=clWhite;
  37.   Canvas.Pen.Color:=clYellow;
  38.   Canvas.Rectangle(0,0,100,100);
  39. end;
  40.  
  41. procedure TMyScrollPanel.WheelDown;
  42. begin
  43.   //
  44. end;
  45.  
  46. procedure Tcib.FormCreate(Sender: TObject);
  47. begin
  48.   MyPanel:=TMyScrollPanel.Create(Self);
  49.   MyPanel.Parent:=Self;
  50.   MyPanel.Align:=alRight;
  51. end;
  52.  
  53. procedure Tcib.FormPaint(Sender: TObject);
  54. begin
  55.   writeln('Paint');
  56. end;          
  57. .
  58. .              
  59.  
trying OnMouseWheelDown:=@WheelDown gives me:
Quote
cib_pas.pas(83,29) Error: Incompatible types: got "<procedure variable type of procedure of object;Register>" expected "<procedure variable type of procedure(TObject;TShiftState;TPoint;var Boolean) of object;Register>"

this case looks like Handoko's example (last post on the first page of this topic) but in my case it doesn't seem to work. What am I overlooking?  :-[
Since I'm inheriting from other class, as @rvk mention in previous post, I'd  rather override some procedure like MouseWheelown (as I did with Click and Paint) but I have no clue how to approach it. Any piece of advice?

thanks in advance!

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: trying to create an own component but getting SIGSEGV
« Reply #35 on: February 03, 2024, 11:24:02 pm »
If you have the unit in DELPHI mode, you can not use the @ in front.

Otherwise, you need to use the @ in fpc mode.
The only true wisdom is knowing you know nothing

rvk

  • Hero Member
  • *****
  • Posts: 6111
Re: trying to create an own component but getting SIGSEGV
« Reply #36 on: February 03, 2024, 11:28:45 pm »
If you have the unit in DELPHI mode, you can not use the @ in front.

Otherwise, you need to use the @ in fpc mode.
No, that's not it. The problem is that you need a procedure like
procedure(TObject;TShiftState;TPoint;var Boolean)

It says so in the error message.
You now only have a procedure without parameters.

And if you want to do it like DoClick instead of the OnXX event you can look in the source which procedure calls this and override it. I'm not behind my computer at the moment so I can't check but will get back to you with a more complete example and explanation how to find the correct procedure name to override.

rvk

  • Hero Member
  • *****
  • Posts: 6111
Re: trying to create an own component but getting SIGSEGV
« Reply #37 on: February 04, 2024, 12:05:52 am »
Since I'm inheriting from other class, as @rvk mention in previous post, I'd  rather override some procedure like MouseWheelown (as I did with Click and Paint) but I have no clue how to approach it. Any piece of advice?
Ok, I'm behind my computer... so...

Here are the stept to take if you want to ASSIGN the OnMouseWheel event.
Do note: If you do this, the one using the component CAN'T assign it's own.
That's why the overriding the DoMouseWheel might be a better option because then you can leave the OnMouseWheel intact for the user.

Put your cursor on the text .OnMouseWheel
Press Alt + Arrow up
If you are at a line with "property OnMouseWheel"
Press Alt + Arrow up again
Now if you are at the line with "property OnMouseWheel: TMouseWheelEvent"
   you are at the correct place.
Put the cursor on TMouseWheelEvent
Press Alt + Arrow up
Now you see the definition of the Wheel event
Code: Pascal  [Select][+][-]
  1. TMouseWheelEvent = procedure(Sender: TObject; Shift: TShiftState;
  2.          WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean) of Object;
So if you want to assign the .OnMouseWheel to a procedure you need to make a procedure like:

Code: Pascal  [Select][+][-]
  1. procedure MyMouseWheelProc(Sender: TObject; Shift: TShiftState;
  2.          WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean) of Object;
And assign it via (depending on fpc or delphi mode)
Code: Pascal  [Select][+][-]
  1. Self.OnMouseWheel := @MyMouseWheelProc;
  2. // or
  3. Self.OnMouseWheel := MyMouseWheelProc;

~~
Now for the overriding of the DoMouseWheel.
In the source where you got the TMouseWheelEvent you can search for "MouseWheel("
You'll find that there is a function DoMouseWheel. You want to override that one.

Copy that line to your component class and create a override function (change the virtual to override).

Code: Pascal  [Select][+][-]
  1.   protected
  2.     function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean; override; // <- OVERRIDE
  3.   //....
  4.  
  5. function MyComponent.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean;
  6. begin
  7.   Result := inherited; // <--- don't forget this. This is the old DoMouseWheel so you probably want to keep that functionality
  8.                        // that old DoMouseWheel also calls the user-assigned OnMouseWheel
  9.  
  10.   // now do your thing
  11.  
  12. end;

(all this is typed without checking so I hope it's correct.)
« Last Edit: February 04, 2024, 12:07:38 am by rvk »

r.lukasiak

  • Full Member
  • ***
  • Posts: 138
Re: trying to create an own component but getting SIGSEGV
« Reply #38 on: February 04, 2024, 12:08:49 am »
thanks for quick reply.

@jamie, yes, I'm {$mode objfpc} so I use @.

@rvk ok, so the parameters are what I overlooked  :-[ I was following Handoko's example but I missed the fact that Click doesn't take parameters.
Now it's working as intended. But I'm still now sure how I can find the structure for the procedure I wanna override? Now I dropped a Panel on the form and peeked on the code it auto-generated. I've been scanning the code since last night and I wasn't able to find anything that would say procedure OnMouseWheel....( and the parameters )


Quote
but will get back to you with a more complete example and explanation how to find the correct procedure name to override.
I'd appreciate it a lot!

cheers!

rvk

  • Hero Member
  • *****
  • Posts: 6111
Re: trying to create an own component but getting SIGSEGV
« Reply #39 on: February 04, 2024, 12:22:37 am »
Our posts crossed (see my explanation here https://forum.lazarus.freepascal.org/index.php/topic,60295.msg505101.html#msg505101).

And yes, sometimes it is Do instead of On and sometimes its without the On.

So you could find Paint as main procedure but sometimes it is DoPaint.

At least with the alt+up you can jump to the definition in the source.
And with ctrl+down you can jump to the implementation (when on the definition/interface).

r.lukasiak

  • Full Member
  • ***
  • Posts: 138
Re: trying to create an own component but getting SIGSEGV
« Reply #40 on: February 04, 2024, 03:30:35 am »
Quote
Put your cursor on the text .OnMouseWheel
Press Alt + Arrow up
If you are at a line with "property OnMouseWheel"
Press Alt + Arrow up again
Now if you are at the line with "property OnMouseWheel: TMouseWheelEvent"
   you are at the correct place.
Put the cursor on TMouseWheelEvent
Press Alt + Arrow up
Now you see the definition of the Wheel event
That's how I was actually doing this but I'd just click on OnMouseWheel, I wouldn't go further to TMouseWheelEvent. That's why I couldn't find the parameters needed for any procedure. Now I tried and I could find the procedure and parameters it takes.

Quote
And yes, sometimes it is Do instead of On and sometimes its without the On.

So you could find Paint as main procedure but sometimes it is DoPaint.
Indeed I found functions  and procedures with "Do" and some without. I even found DoMouseWheel while desperately searching the source and I tried to override it the way you showed me but first, I did it without parameters and second ,I did it as procedure, not a function so I ended up with "There is no method in an ancestor class to be overridden".

I've just tried both solutions and they are working flawlessly. Thank you very much for detailed explanation and clear examples.

Just for the sake of better understanding... why Result := inherited; and not just inherited? Is it because it's a function, not a procedure?

rvk

  • Hero Member
  • *****
  • Posts: 6111
Re: trying to create an own component but getting SIGSEGV
« Reply #41 on: February 04, 2024, 10:07:12 am »
Just for the sake of better understanding... why Result := inherited; and not just inherited? Is it because it's a function, not a procedure?
Yes. Normally such event report if the action (wheel in this case) is handled.

In the case of DoMouseWheel, if the Result (handled) is not set to true, the OnMouseWheelUp and Down are tried (although they don't get the WheelDelta that this one gets).

So when creating a component it is wise to look into the documentation and source code to see what is done and expected of that function.

Documentation for DoMouseWheel is here
https://lazarus-ccr.sourceforge.io/docs/lcl/controls/tcontrol.domousewheel.html

And this is the description for the DoMouseWheel of the TDBGrid
https://lazarus-ccr.sourceforge.io/docs/lcl/grids/tcustomgrid.domousewheel.html

r.lukasiak

  • Full Member
  • ***
  • Posts: 138
Re: trying to create an own component but getting SIGSEGV
« Reply #42 on: February 05, 2024, 07:11:09 pm »
ok, I got the general idea, thank you very much. For now, everything is working as expected.

Maybe I could pick your brain to solve another issue I recently faced, it's also class-related (I think) and also resulting in SIGSEGV.

I have this:
Code: Pascal  [Select][+][-]
  1. TGame = class
  2.     private
  3.       .
  4.       .
  5.       BoardArray: array of array of TPuzzle;
  6.       Pic: TBitmap;
  7.     public
  8.       constructor Create(gs: integer; PicNo: integer);
  9.       destructor Destroy;  
  10.   end;      
  11.  
  12. TPuzzle = class (TImage)
  13.     private
  14.       .
  15.       .
  16.       x,y: byte;
  17.     public
  18.       PuzzleNo: integer;
  19.       procedure DrawMe;
  20.       .
  21.       .
  22.   end;  
  23.  
  24. .
  25. .
  26. .
  27. procedure TPuzzle.DrawMe;
  28. var
  29.   bmp: TBGRABitmap;
  30. begin
  31.   bmp:=TBGRABitmap.Create(Width,Height);
  32.  
  33.   bmp.Canvas.CopyRect( Rect(0,0,bmp.Width-1,bmp.Height-1),
  34.                        Game.Pic.Canvas, //<-- this line is pointed out as a culprit of SIGSEGV
  35.                        Rect(x*Width, y*Height,
  36.                        ((y+1)*Width)-1, ((x+1)*Height)-1)
  37.                        );
  38.   //writeln(Game.Pic.Width);    //<--even this is causing SIGSEGV
  39.  
  40.  
  41. end;  
  42.  
any reference to Game.Pic. from inside of DrawMe ends up in SIGSEGV. It could be solved by passing the image to DrawMe as parameter but I think it's not necessary, I believe Game.Pic should be visible and usable from DrawMe as it's a method in TPuzzle which is a part of TGame. What am I missing?

both Game and Puzzle are previously created. I also tried to make pic public for better "visibility" but it didn't do the trick.

any clue?

rvk

  • Hero Member
  • *****
  • Posts: 6111
Re: trying to create an own component but getting SIGSEGV
« Reply #43 on: February 05, 2024, 07:20:35 pm »
Maybe I could pick your brain to solve another issue I recently faced, it's also class-related (I think) and also resulting in SIGSEGV.
Without seeing more of the code there is nothing to say for certain.

Is Game at the point of DrawMe a valid pointer?
Is Game.Pic filled with something valid at the moment of DrawMe.

Probably not but because we don't see enough code we can't say why.
All I can say for certain is that Game or Game.Pic is invalid or nil at that moment.

r.lukasiak

  • Full Member
  • ***
  • Posts: 138
Re: trying to create an own component but getting SIGSEGV
« Reply #44 on: February 05, 2024, 08:50:47 pm »
Quote
Is Game at the point of DrawMe a valid pointer?
I think so, Game is created in onClick event:
Code: Pascal  [Select][+][-]
  1. procedure TPuzzles.NewBtnClick(Sender: TObject);
  2. var i,j: integer;
  3. begin
  4. .
  5. .
  6. Game:=TGame.Create(NewGame.BCComboBox1.ItemIndex+3,NewGame.PicNo);
  7. .
  8. .
  9. end;
  10.  

Quote
Is Game.Pic filled with something valid at the moment of DrawMe.
Game.Pic is created within it constructor.
Code: Pascal  [Select][+][-]
  1. constructor TGame.Create(gs: integer; PicNo: integer);
  2. var i,j: integer;
  3.   c: TBGRAPixel;
  4.   bgrabmp: TBGRABitmap;
  5. begin
  6.   Writeln('TGame.Create');
  7.   Pic:=TBitmap.Create;
  8.   Pic.PixelFormat := pf32bit;
  9.  
  10.       if NewGame.PicNo = 0 then
  11.         begin
  12.           writeln('PicNo=0 so drawing the image');
  13.           bgrabmp:=TBGRABitmap.Create(dm.imgs.Width,dm.imgs.Height);
  14.           bgrabmp.GradientFill(0,0,dm.imgs.Width,dm.imgs.Height{ div 1},
  15.                            BGRA(80,80,80,192), BGRA(160,160,160,224), gtLinear,
  16.                            PointF(0,0), PointF(dm.imgs.Width,0),
  17.                            dmSet,true,False,daFloydSteinberg);
  18.           bgrabmp.GradientFill(0,dm.imgs.Width div 2,dm.imgs.Width,dm.imgs.Height,
  19.                            BGRA(80,80,80,192), BGRA(160,160,160,224), gtLinear,
  20.                            PointF(0,0), PointF(dm.imgs.Width,0),
  21.                            dmSet,true,False,daNearestNeighbor);
  22.           Pic.Assign(bgrabmp);
  23.  
  24.           bgrabmp.Free;
  25.           write('Assigned(Pic)= ');
  26.           if assigned(Pic) then writeln('true   with width='+Pic.Width.ToString)
  27.                            else writeln('false');
  28.         end
  29.         else
  30.         begin
  31.           writeln('PicNo>0 so loading the image');
  32.           dm.imgs.GetBitmap(PicNo, Pic );
  33.           write('Assigned(Pic)= ');
  34.           if assigned(Pic) then writeln('true   with width='+Pic.Width.ToString)
  35.                            else writeln('false');
  36.         end;              
  37. .
  38. .
  39. SetLength(BoardArray,GameSize,GameSize);
  40.   for i:=0 to GameSize-1 do
  41.     for j:=0 to GameSize-1 do
  42.       if (i=GameSize-1) and (j=GameSize-1) then
  43.       begin
  44.         BoardArray[j,i]:=nil;
  45.         Empty.x:=j;
  46.         Empty.y:=i;
  47.       end
  48.       else
  49.       begin
  50.         //create the puzzle
  51.         BoardArray[j,i]:=TPuzzle.Create(Puzzles);
  52.         .
  53.         .
  54.         BoardArray[j,i].DrawMe;      
  55. end;
  56.  
now I added some writelns to make sure Pic is created and there is any image with size>0 and both are true.

but now I'm starting thinking about Game.Pic.Canvas itself. Game is a "living instance" of TGame but what I'm actually doing here is the class (TGame) definition, so I'm not sure if I should refer to the instance. But if I can't, how else I could refer to a var in TGame? I'm not sure if I expres my thoughts clerly  :-[

 

TinyPortal © 2005-2018