Link to home
Start Free TrialLog in
Avatar of bryan7
bryan7Flag for Japan

asked on

capture every mouse move in a TImage ( x,y )

I want to capture every mouse move inside a TImage, I mean every x,y position.


OnMouseMove is too slow and doesn't get at time all the moves..

bryan
Avatar of kambiz
kambiz

Windows does not generate WM_MOUSEMOVE for all mouse movements.

Kambiz
ASKER CERTIFIED SOLUTION
Avatar of czyky
czyky

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
You probably know this and need more positional info by the sound of it but h ere's a way that gets the current posion when you need it.

Set a timer off and ontimer find the current cursor position  using GetCursorPos(P)
Then compute relevant posion to your image using etc.
x := P.x - MyImage.ClientOrigin.x;
y := P.x - MyImage.ClientOrigin.y;

Maybe you need the memory of every posion the mouse has been to..
Hmm.. Thinking

Steve
Avatar of bryan7

ASKER

Adjusted points to 25
Avatar of bryan7

ASKER

I want to do a paint program.. i need to: catch every x,y of the mouse move INSIDE a TImage, draw a pixel there, and send the position over a socket to have a syncronized drawing board over tcp/ip

can I have an example on using the hook ? I never used hooks..
btw, I have RxLib which has a component that catches messages I think.. maybe I can use that ?

thanks
What about doing it a different way..
How about doing a regular paint  program using the timer method above. Then send just information about the changes to the other end?
Avatar of bryan7

ASKER

"just information about the changes to the other end? " 

yeah, but how do I paint ? when I move the mouse over the image i check if the left butt is pressed, but if I can't get every move I get only distant points in stead of lines..
OK..
Make a new app and drop a TImage on Form1.  Go to the Object inspector and double click the three events shown below. Then add the TRect variable to the forms private section (or public if you want to 'see' this var from other units). Now cut and past the contents of the event handlers from below. Have a good play with it.  Lot of things demo'd in there.
You would have to extend it with the timer technique shown earlier so the program can do updates as often as you need it within scope of performance.  
Then during each draw (on timer)you must send the coordinates when mouse goes down and while mouse is down is down then coord when mouse is up agian. Other computer then does the same draw code but from received coords.

Let me know how you get on. I can help more..

Note, a professional program would more likely use a TPaintBox and update it from it's onpaint event from a TBitmap which is acting as a back buffer. Then during the OnPaint the system will wait for the video trace to reach the bottom of the screen before painting the paintbox with the bitmap to avoid tearing on screen (flicker). This demo just paints straight onto the image and the image by definition holds a copy of the picture. A paintbox would not so has to be kept painted. The backbuffer would be painted via synchronize method in seperate thread. This makes stuff in the image keep moving smooth while user selects menus etc. Still, walk first, run later!

the demo..
//=========
unit RubberBandUnit;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
  Controls, Forms, Dialogs, ExtCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    procedure Image1MouseDown(Sender: TObject; Button:
      TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure Image1DragOver(Sender, Source: TObject;
      X, Y: Integer; State: TDragState; var Accept: Boolean);
    procedure Image1EndDrag(Sender, Target: TObject; X,Y: Integer);
  private
    { Private declarations }
    TheRect: TRect;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Image1MouseDown(Sender: TObject; Button:
  TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  TheRect := Bounds(X, Y, 0, 0);
  with Image1.Picture.Bitmap.Canvas do
  begin
    Brush.Style := bsClear;
    Pen.Color   := clWhite;
    Pen.Mode    := pmXor;
  end;
  Image1.BeginDrag(True);
end;

procedure TForm1.Image1DragOver(Sender, Source: TObject;
  X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
  if (Source <> Sender)
    then Accept := False
    else
      with Image1.Picture.Bitmap.Canvas do
        with TheRect do
        begin
          Rectangle(Left, Top, Right, Bottom);
          BottomRight := Point(X,Y);
          Rectangle(Left, Top, Right, Bottom);
        end;
end;

procedure TForm1.Image1EndDrag(Sender, Target: TObject;
  X, Y: Integer);
begin
  if Target = Sender then // draw rectangle
  with Image1.Picture.Bitmap.Canvas do
  begin
    Pen.Color := clYellow;
    Pen.Mode  := pmCopy;
    Rectangle(TheRect.Left, TheRect.Top, X, Y);
  end
  else  // erasing
    with Image1.Picture.Bitmap.Canvas do
      with TheRect
        do Rectangle(Left, Top, Right, Bottom);
end;

end.
//=====

Steve
listening...
Avatar of bryan7

ASKER

Adjusted points to 40
Avatar of bryan7

ASKER

SteveWaite.. thanks,, but .. maybe I missed something ? I tried the code but all I get is the mouse cursor changing.. I don't get any drawing..

what am I doing wrong ? must I do something more than that code ? I don't understand at all what you proposed about the timer,,
OK,

I left you with a little too much to find out.  Don't worry, nearly there.

You could load up an image using the object inspector by double clicking the 'Picture' property of Image1 and you would have seen some drawing!  Basically the image was uninitialised.

Here I’ve added the create event to the form and made the image fit the forms' client area and filled it with a color.

Now add this to the OnCreate event yourself..
//======
procedure TForm1.FormCreate(Sender: TObject);
begin
  Image1.Align := alClient;
  with Image1.Canvas do
  begin
    Pen.Color   := clBlack;
    Brush.Color := clBlack;
    FillRect(Rect(0, 0, Image1.Width, Image1.Height));
  end;
end;
//======

Now have a jolly good play with it.  Change colors and pen/brush styles etc. to see what the effects are.

Now this app simply draws everything as a result of normal screen updating, which will be too often for your proposed app.  So the timer method is would have to be employed in the end.

Now have a look at the graphex demo in ~\Delphi 3\Demos\DOC\GRAPHEX for loads more examples.  But that too just does all updates in form paint event.

To go further on..

Here is a line drawing app I knocked up quickly using a backbuffer, paintbox and timer.  (In your app you can send co-ordinate information to the other computer in ontimer event).

This is in straight line drawing mode You need to add different drawing modes to accommodate drawing dots, rectangles (like rubber band) and ellipses (use co-ordinates of rectangle but draw ellipse) etc.

Start a fresh app and drop a paintbox and timer on the form and flesh it out with the stuff below…

//=======
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    PaintBox1: TPaintBox;
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure PaintBox1Paint(Sender: TObject);
    procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

const
  UpdateInterval = 100;

var
  Form1: TForm1;
  BackBuffer: TBitmap;
  x1, y1: Integer;  // stores position when  on mouse down
  x2, y2: Integer;  // stores position when mouse moving
  MouseIsDown: Boolean;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  MouseIsDown := False;
  x1 := 0;
  y1 := 0;
  x2 := 0;
  y2 := 0;

  PaintBox1.Left := 0;
  PaintBox1.Top := 0;
  PaintBox1.Width := ClientWidth;  // note use of ClientWidth, not Width
  PaintBox1.Height := ClientHeight;

  BackBuffer := TBitmap.Create;
  BackBuffer.Width := PaintBox1.ClientWidth;
  BackBuffer.Height := PaintBox1.ClientHeight;
  with BackBuffer.Canvas do
  begin
    Pen.Color := clBlack;
    Brush.Color := clBlack;
    FillRect(Rect(0, 0, BackBuffer.Width, BackBuffer.Height));
  end;

  Timer1.Interval := UpdateInterval;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  with PaintBox1.Canvas
    do Draw(0, 0, BackBuffer);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  BackBuffer.Free;
end;

procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
  with PaintBox1.Canvas
    do Draw(0, 0, BackBuffer);
end;

procedure TForm1.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  MouseIsDown := True;
  x1 := X;
  y1 := Y;
  x2 := X;
  y2 := Y;
end;

procedure TForm1.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  if MouseIsDown then
  begin
    with BackBuffer.Canvas do
    begin
      Brush.Style := bsClear;
      Pen.Color := clWhite;
      Pen.Mode := pmXor;
      MoveTo(x1, y1);
      LineTo(x2, y2);
      MoveTo(x1, y1);
      LineTo(X, Y);
    end;
    x2 := X;
    y2 := Y;
  end;
end;

procedure TForm1.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  MouseIsDown := False;
end;

end.
//=======

Now have another good mess about and let me know how you get on!

Steve.

How ya doin bryan!