Search Tips Advanced Search |
| Delphi Graphics and Game Programming Exposed with DirectX 7.0 by John Ayres Wordware Publishing, Inc. ISBN: 1556226373 Pub Date: 12/01/99
Search this book:
Previous | Table of Contents | Next
| Tip: The dwTimeStamp member of this structure indicates when the input event actually occurred. The time stamps from subsequent events could potentially be used to determine if the user double-clicked on some item. The dwSequence number, on the other hand, indicates the chronological sequence in which input events where obtained. This is useful for determining the order of input from two separate devices, as DirectInput never assigns the same sequence number for events from different devices, even if they have the same time stamp. This might come in handy if developing a multiplayer game where two people can play from the same machine.
All of this new functionality has been added to our baseline application so we can easily create DirectX applications with mouse support. While we are not going to cover it here, this new baseline application contains some additional code for drawing the mouse cursor on the primary surface from the secondary input thread. This solves the problem we noted earlier about the mouse cursor dependency on the animation frame rate. The following listing illustrates using the event notification and buffered data retrieval methods for obtaining mouse input data. Listing 7-10: Retrieving buffered mouse input data
    interface

    type

       .

       .

       .

      {this class is used to track click events}

      TMouseData = class

      public

        XPos, YPos: Integer;

        Button: TMouseButton;

        constructor Create(X, Y: Integer; MButton: TMouseButton);

      end;

    var

      CurX, CurY, OldX, OldY: Integer;        // the mouse position variables

      MouseClickList: TList;                  // the list of click events

      FCritSec: TCriticalSection;             // syncronization object

      FMouseEvents: TWOHandleArray;           // events array

    const

      {we’re using a 32 X 32 pixel mouse cursor image}

      CURSORWIDTH = 32;

      CURSORHEIGHT = 32;

      {our animated mouse cursor has 8 frames}

      NUMFRAMES = 8;

    implementation

    constructor TMouseData.Create(X, Y: Integer; MButton: TMouseButton);

    begin

      {create and initialize the mouse data object for a click event}

      XPos := X;

      YPos := Y;

      Button := MButton;

    end;

    procedure TMouseThread.Execute;

    var

      EventNum: Integer;

      NumElements: DWORD;

      DeviceData: TDIDeviceObjectData;

      DataResult: HRESULT;

      SourceRect,

      DirtyRect: TRect;

    begin

      {continuously perform this code until terminated}

      while not Terminated do

      begin

        {wait until one of the events becomes signaled}

        EventNum := WaitForMultipleObjects(2, @FMouseEvents, FALSE, INFINITE);

        {was the quit event signaled?  if so, terminate}

        if EventNum-WAIT_OBJECT_0 = QUITEVENT then

        begin

          Terminate;

          Break;

        end;

        {we are about to use global variables, so enter the critical section}

        FCritSec.Enter;

        {store the current mouse cursor coordinates}

        OldX := CurX;

        OldY := CurY;

        {begin retrieving buffered data}

        while TRUE do

        begin

          {we only want to retrieve one element at a time}

          NumElements := 1;

          {retrieve the buffered data}

          DataResult := FMouseDevice.GetDeviceData(SizeOf(TDIDeviceObjectData),

                                                   @DeviceData, NumElements, 0);

          {if we retrieved data, update global variables}

          if (DataResult = DI_OK) and (NumElements = 1) then

          begin

            case DeviceData.dwOfs of

              {update mouse cursor positions}

              DIMOFS_X        : CurX := CurX + DeviceData.dwData;

              DIMOFS_Y        : CurY := CurY + DeviceData.dwData;

              {update our list of click events}

              DIMOFS_BUTTON0  : MouseClickList.Add(TMouseData.Create(CurX, CurY,

                                                                     mbLeft));

              DIMOFS_BUTTON1  : MouseClickList.Add(TMouseData.Create(CurX, CurY,

                                                                     mbRight));

            end;

          end

          else

            {if there were no move input events to retrieve, continue}

            Break;

        end;

        {clip the global mouse cursor positions to the extents of our

         screen resolution}

        if CurX < 0 then

           CurX := 0;

        if CurX > DXWIDTH-CURSORWIDTH then

           CurX := DXWIDTH-CURSORWIDTH-1;

        if CurY < 0 then

           CurY := 0;

        if CurY > DXHEIGHT-CURSORHEIGHT then

           CurY := DXHEIGHT-CURSORHEIGHT-1;

        {if the mouse position has changed...}

        if not ((CurX = OldX) and (CurY = OldY)) then

        begin

          {draw the mouse cursor if the old and new positions do not overlap...}

          if (ABS(CurX - OldX)>= CURSORWIDTH) or

             (ABS(CurY - OldY)>= CURSORHEIGHT) then

          begin

            {erase the old cursor}

            SourceRect := Rect(NUMFRAMES*CURSORWIDTH, 0, NUMFRAMES*CURSORWIDTH+

                               CURSORWIDTH, CURSORHEIGHT);

            FPrimarySurface.BltFast(OldX, OldY, FMouseCursors, SourceRect,

                                    DDBLTFAST_NOCOLORKEY or DDBLTFAST_WAIT);

            {save area under new cursor location}

            SourceRect := Rect(CurX, CurY, CurX+CURSORWIDTH, CurY+CURSORHEIGHT);

            FMouseCursors.BltFast(NUMFRAMES*CURSORWIDTH, 0, FPrimarySurface,

                                  SourceRect, DDBLTFAST_NOCOLORKEY or

                                  DDBLTFAST_WAIT);

            {draw new cursor}

            SourceRect := Rect(FCursorFrame*CURSORWIDTH, 0, (FCursorFrame*

                               CURSORWIDTH)+CURSORWIDTH, CURSORHEIGHT);

            FPrimarySurface.BltFast(CurX, CurY, FMouseCursors, SourceRect,

                                    DDBLTFAST_NOCOLORKEY or DDBLTFAST_WAIT);

          end

          else

          {...or draw the mouse cursor if the old and new positions do overlap}

          begin

            {copy the dirty rectangle to the scratch buffer}

            UnionRect(DirtyRect, Rect(OldX, OldY, OldX+CURSORWIDTH,

                                      OldY+CURSORHEIGHT),

                      Rect(CurX, CurY, CurX+CURSORWIDTH, CurY+CURSORHEIGHT));

            FMouseScratch.BltFast(0, 0, FPrimarySurface, DirtyRect,

                                  DDBLTFAST_NOCOLORKEY or DDBLTFAST_WAIT);

            {erase the old cursor from the scratch buffer}

            SourceRect := Rect(NUMFRAMES*CURSORWIDTH, 0, NUMFRAMES*CURSORWIDTH+

                               CURSORWIDTH, CURSORHEIGHT);

            FMouseScratch.BltFast(OldX-DirtyRect.Left, OldY-DirtyRect.Top,

                                  FMouseCursors, SourceRect, DDBLTFAST_NOCOLORKEY or

                                  DDBLTFAST_WAIT);

            {save the area under the new cursor}

            SourceRect := Rect(CurX-DirtyRect.Left, CurY-DirtyRect.Top, (CurX-

                               DirtyRect.Left)+CURSORWIDTH, (CurY-

                               DirtyRect.Top)+CURSORHEIGHT);

            FMouseCursors.BltFast(NUMFRAMES*CURSORWIDTH, 0, FMouseScratch,

                                  SourceRect, DDBLTFAST_NOCOLORKEY or

                                  DDBLTFAST_WAIT);

            {draw new cursor into scratch buffer}

            SourceRect := Rect(FCursorFrame*CURSORWIDTH, 0, (FCursorFrame*

                               CURSORWIDTH)+CURSORWIDTH, CURSORHEIGHT);

            FMouseScratch.BltFast(CurX-DirtyRect.Left, CurY-DirtyRect.Top,

                                  FMouseCursors, SourceRect, DDBLTFAST_NOCOLORKEY or

                                  DDBLTFAST_WAIT);

            {draw scratch buffer back to primary buffer}

            SourceRect := Rect(0, 0, DirtyRect.Right-DirtyRect.Left,

                               DirtyRect.Bottom-DirtyRect.Top);

            FPrimarySurface.BltFast(DirtyRect.Left, DirtyRect.Top, FMouseScratch,

                                    SourceRect, DDBLTFAST_NOCOLORKEY or

                                    DDBLTFAST_WAIT);

          end;

        end;

        {we are finished using global variables, so we can now leave

         the critical section}

        FCritSec.Leave;

      end;

    end;
Previous | Table of Contents | Next

Products | Contact Us | About Us | Privacy | Ad Info | Home Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.