views:

791

answers:

6

In a Delphi 7 application, I want to move a component accordingly to the mouse. Im doing something like that:

procedure MyComponent.MouseMove(Sender: TObject;Shift: TShiftState; X, Y: Integer);
begin
  AnotherComponent.Top := X;
  AnotherComponent.Left := Y;
end;

When I move the mouse the CPU usage for the main core goes up to 100% on a recent PC.

Any idea or tick to reduce the CPU usage in this case ?

+4  A: 

You could create a TTimer that polls the current mouse position every 0.10 seconds or so, then positions "AnotherComponent" according to the current mouse position.

Then you wouldn't fire your event for every pixel of mouse movement- you won't need any OnMouseMove event on your controlling component at all.

On my computer, this basically has no performance impact at all.

procedure TForm1.Timer1Timer(Sender: TObject);
var
  pt: TPoint;
begin
  //Is the cursor inside the controlling component?  if so, position some
  //other control based on that mouse position.

  GetCursorPos(pt);
  if MouseWithin(pt.x,pt.y,MyComponent,Form1.Left,Form1.Top) then begin
    //replace with whatever real positioning logic you want
    AnotherComponent.Top := pt.y;
    AnotherComponent.Left := pt.x;
  end;
end;

function TForm1.MouseWithin(mouseX, mouseY: integer;
  const comp: TWinControl; const ParentWindowLeft: integer;
  const ParentWindowTop: integer): boolean;
var
  absoluteCtrlX, absoluteCtrlY: integer;
begin
  //take a control, and the current mouse position.
  //tell me whether the cursor is inside the control.
  //i could infer the parent window left & top by using ParentwindowHandle
  //but I'll just ask the caller to pass them in, instead.

  //get the absolute X & Y positions of the control on the screen
  //needed for easy comparison to mouse position, which will be absolute
  absoluteCtrlX := comp.Left + ParentWindowLeft;
  absoluteCtrlY := comp.Top + ParentWindowTop +
    GetSystemMetrics(SM_CYCAPTION);

  Result := (mouseX >= absoluteCtrlX)
    and (mouseX < absoluteCtrlX + comp.Width)
    and (mouseY >= absoluteCtrlY)
    and (mouseY <= absoluteCtrlY + comp.Height);
end;
JosephStyons
A: 

It can't be the move itself that needs so much cpu power, most probably the move causes the component to redraw itself somehow. Can you avoid that AnotherComponent is redrawn on each move? It should not be necessary, unless it is a movie container.

Treb
A: 

Anything tied to the mouse move event will be called very frequently as mice are a high resolution input device. I wouldn't worry about the cpu usage though because your handler only gets fired as fast as possible based on how busy the system is. In other words, it's only maxing the CPU because nothing else is.

From MSDN:

The mouse generates an input event when the user moves the mouse, or presses or releases a mouse button. The system converts mouse input events into messages and posts them to the appropriate thread's message queue. When mouse messages are posted faster than a thread can process them, the system discards all but the most recent mouse message.

Now there may be some exceptions to this. You could do some testing to be sure by running some other processing intensive activity and see how much the mouse move stuff impacts it.

Arnold Spence
Down votes without an explanation are not as constructive as they could be. If you can identify something wrong, please let us know. I will happily make a correction to my answer.
Arnold Spence
I did not downvote you, however your POV is not multi-tasking-friendly. The GUI thread will have normal priority, so it will completely starve all threads with lower priority, and take away cycles from threads with same priority. Optimizing for lowest CPU load is important, even if unfashionable.
mghie
That may be true as a general rule but this is a GUI process. Interaction with the user generally always takes precedence as responsiveness is a priority. It looks like the author is trying to animate a mouse drag operation. Wouldn't want that jumping periodically out of sync with the mouse.
Arnold Spence
+3  A: 
  1. It has nothing to do with the Mouse Move itself.
  2. Unless it's what you intended, you are mismatching X, Y with Top, Left. Top is the Y coord and Left the X one.
  3. The problem is the actual moving of AnotherComponent.

To try and understand it, I suggest that you write a TestMove routine that moves your AnotherComponent automatically with adjustable repetition/delays to monitor the CPU.
I bet it triggers a costly repaint or some other CPU intensive calculation.
So Examine closely if you have any event handler on this component first, then go with the inherited behavior...

François
+1  A: 

Maybe, instead of moving the component itself you move a 'shadow' and only move the component once the user lets the mousebutton go. Sort of like drag&drop.

+3  A: 

Finally I've change my code for this one:

procedure MyComponent.MouseMove(Sender: TObject;Shift: TShiftState; X, Y: Integer);
begin
  if GetTickCount-LastMoveTick>50 then begin
    AnotherComponent.Top := Y;
    AnotherComponent.Left := X;
    LastMoveTick := GetTickCount;
  end;
end;

Really easy to implement (2 lines added), no timer, works well for me...

Pierre-Jean Coudert
mj2008
Corrected. thanks !
Pierre-Jean Coudert
I like this better than my solution :)
JosephStyons