OpenWindows Server Device Developer's Guide
  Rechercher uniquement dans ce livre
Télécharger cet ouvrage au format PDF

Cursors

4

Cursor implementations for most device handlers fall into one of these categories:
  • Software cursor
  • Limited-size hardware cursor
You can use a number of software layers to help with your cursor implementation, depending on your graphics adapter hardware. This chapter helps you choose the cursor layer that is best for your hardware. The porting interface for each of the available layers is also discussed in detail.

The Basic DDX Interface

The basic DDX interface describing cursor routines for a screen is defined in the MIT sample server document Definition of the Porting Layer for the Xv11 Sample Server. This interface consists of the following functions:

  pScreen->RealizeCursor(pScr, pCurs)  
  pScreen->UnrealizeCursor(pScr, pCurs)  
  pScreen->DisplayCursor(pScr, pCurs)  
  pScreen->RecolorCursor(pScr, pCurs, displayed)  
  pScreen->ConstrainCursor(pScr, pBox)  
  pScreen->PointerNonInterestBox(pScr, pBox)  
  pScreen->CursorLimits(pScr, pCurs, pHotBox, pTopLeftBox)  
  pScreen->SetCursorPosition(pScr, newx, newy, generateEvent)  

It is possible for your DDX handler to port directly at this level. You can do this by supplying fully customized versions of these functions in your screen initialization routine.
A DDX implementation of these cursor functions is provided in utility layers discussed in the remainder of this chapter. If your graphics device is an MPG (multiple plane group) device and your cursor implementation is in a separate plane group, refer to Chapter 5, "Multiple Plane Group Interface."

Note - Due to implementation constraints in the server, the Sun mouse implementation requires you to initialize the mipointer code in your DDX handler. The following miPointer routines are used by the ddxSUNWmouse device handler.
· miPointerGetMotionEvents
· miPointerGetMotionBufferSize
· miPointerDeltaCursor
· miPointerPosition
· miPointerAbsoluteCursor


Software Cursor

This section describes the software cursor porting interface for your DDX handler.

miDC Layer

The mi utility layer provides a software cursor implementation in the miDC (mi Display Cursor) layer. If your display adapter does not have any hardware cursor capability, a complete software cursor implementation can be enabled by calling the miDCInitialize function in your Screen initialization routine.
SPARC: For an example of a software cursor implementation, see the cg3 reference DDX handler in the server/ddx/solaris/reference/cg3 directory.
x86: For an example of a software cursor implementation, see the v256 reference DDX handler in the server/ddx/solaris/reference/v256 directory.
Call the miDCInitialize function after most of the Screen functions have been initialized. It uses the miSprite layer that wraps most of the Screen functions. See the sample cg3 or v256 handler for an example of the order in which to call the Screen initialization functions.
Call the miDCInitialize routine with the following parameters:

  #include "mipointer.h"  
  ...  
  miDCInitialize(ScreenPtr pScreen,  
           miPointerScreenFuncPtr screenFuncs);  

The sun layer provides a set of screenFuncs that is an array of pointers to functions required by the miPointer layer (such as CursorOffScreen, CrossScreen and WarpCursor).
The following example is all that is required in your DDX handler to enable the software cursor implementation in the mi layer.

  #include "sun.h"  
  ...  
  #include "mipointer.h"  
  ...  
  ...  
  extern miPointerScreenFuncRec sunPointerScreenFuncs;  
  ...  
  miDCInitialize(pScreen, &sunPointerScreenFuncs)  

The following sections describe in more detail the mi layers that the miDC layer uses to provide a software cursor. If you are in a hurry to get a software cursor working on your graphics adapter, you do not need to know all of the mi layer details.
The miDC layer internally uses the miSprite and miPointer layers to implement the software cursor.

The miPointer Layer

The miPointer layer offers a set of the basic DDX cursor interface. This means that it supplies an implementation of the DDX eight discussed in "The Basic DDX Interface" on page 25. To get the miPointer layer to work however, you must provide an implementation of miPointerSpriteFuncs and miPointerScreenFuncs. Each of these is an array of four functions that you pass to miPointerInitialize.

  miPointerInitialize(ScreenPtr pScreen,  
           miPointerSpriteFuncPtr spriteFuncs,  
           miPointerScreenFuncPtr screenFuncs, Bool waitForUpdate)  

miPointerSpriteFuncs is a set of four functions that implement the sprite software.

  RealizeCursor(pScr, pCurs)  
  UnrealizeCursor(pScr, pCurs)  
  SetCursor(pScr, pCurs, x, y)  
  MoveCursor(pScr, x, y)  

miPointerScreenFuncs is a set of functions that implement Screen crossings and cursor warping.

  CursorOffScreen(pScr, x, y)  
  CrossScreen(pScr, entering)  
  WarpCursor(pScr, x, y)  
  EnqueueEvent(xEvent)  
  NewEventScreen(pScr)  

Irrespective of which sprite implementation you choose, use the miPointerScreenFuncs implementation provided in the sun layer. The sunPointerScreenFuncs array provides implementations for CursorOffScreen, CrossScreen, and WarpCursor. It has NULL pointers for EnqueueEvents and NewEventScreen; these are initialized by
miPointerInitialize to the routines mieqEnqueue and mieqSwitchScreen. The sunPointerScreenFuncs array is used by including the following code in your DDX handler.

  #include "sun.h"  
  ...  
  #include "mipointer.h"  
  ...  
  ...  
  extern miPointerScreenFuncRec sunPointerScreenFuncs;  

The miSprite Layer

The miSprite layer provides a set of the miPointerSpriteFuncs required to drive the miPointer layer. The miSprite layer offers a software sprite--a software overlay that can be moved around on the screen, while preserving other images on the screen.
The miSprite layer does this by wrapping all the Screen rendering functions and all the GC functions. It saves areas under the sprite, and restores them when the sprite moves. It removes the sprite while rendering occurs to areas under the sprite, and restores the sprite when required. To get miSprite to work, miSpriteInitialize needs to be passed an array of miSpriteCursorFuncs.

  miSpriteInitialize(ScreenPtr pScreen,  
           miSpriteCursorFuncPtr cursorFuncs,  
           miPointerScreenFuncPtr screenFuncs);  

miSpriteCursorFuncs is an array of these functions:

  RealizeCursor(pScr, pCurs)  
  UnrealizeCursor(pScr, pCurs)  
  PutUpCursor(pScr, pCurs, x, y)  
  SaveUnderCursor(pScr, x, y, w, h)  
  RestoreUnderCursor(pScr, x, y, w, h)  
  MoveCursor(pScr, x, y, w, h, dx, dy)  
  ChangeSave(pScr, x, y, w, h, dx, dy)  
  InCursorPlanes(pWin)  

An implementation of these functions is provided by the miDC layer. This layer draws the software cursor image.

Hardware Cursor

This section describes the porting interface for your DDX handler if you have a hardware cursor. The hardware cursor is limited by the size of the cursor image registers.
The X Protocol leaves it up to the server implementation to decide what the cursor looks like if the cursor defined for the Screen exceeds the physical limits imposed by the cursor hardware. Some server implementations choose to trim the cursor image around the hotspot such that it fits into the size limits imposed by the hardware.
Another strategy, and one that is followed by the OpenWindows server, is to revert to a software cursor implementation whenever a cursor defined for a Window does not fit in the hardware. This means that if there are multiple cursors defined on the same screen, some small enough to fit in the hardware cursor registers, and some larger, the cursor dynamically switches between hardware and software forms as the pointer is moved across the screen. This hardware and software cursor switching is implemented in a utility layer in the server, called sunSprite.

The sunSprite Layer

The sunSprite layer implements a sprite that can switch between hardware and software forms. It uses the software cursor layers described in "Software Cursor" on page 26 whenever the cursor does not fit into hardware.
In your DDX handler, you might want to use the sunSprite layer to handle your cursor if you want to switch between hardware and software cursors on the same screen. It is recommended that the cursor defined by the application be displayed as actual size, even if this means that it cannot fit into hardware. This is motivated by the desire to keep the application's look and feel consistent across all graphics adapters supported by the OpenWindows server.
The sunSprite code is initialized in the DDX handler's Screen initialization function by calling the following function:

  #include "sun.h"  
  ...  
  ...  
  Bool sunSpriteInitialize(ScreenPtr pScreen,  
           Bool (*putInHardware)(),  
           miPointerSpriteFuncPtr hardwareSpriteFuncs,  
           miPointerScreenFuncPtr screenFuncs)  

To make the sunSprite layer work, you must pass the sunSprite layer a set of four functions that implement a hardware cursor on your device (miPointerSpriteFuncPtr) and a function that is called by the sunSpriteLayer to check if a defined cursor should be put in hardware or software (putInHardware). An implementation of screenFuncs is already available:

  #include "sun.h"  
  ....  
  #include "mipointer.h"  
  ....  
  ....  
  extern miPointerScreenFuncRec sunPointerScreenFuncs;  

The four functions that implement the hardware cursor and the putCursorInHardware function are needed to port to your hardware.

  Bool xxxPutInHardware(ScreenPtr pScr, CursorPtr pCurs)  

This function returns TRUE if the cursor should be placed in hardware; FALSE if the cursor should be drawn by software (miDC).
The following code is a sample implementation of this function on a device that has a 32x32 cursor register.

  Bool  
  XXXPutInHardware(pScreen, pCursor)  
       ScreenPtr pScreen;  
       CursorPtr pCursor;  
  {  
       if (pCursor->bits->width > 32 || pCursor->bits->height > 32)  
           return FALSE;  
       return TRUE;  
  }  

Examples of miPointerSpriteFuncs

The following code is a sample pseudo-implementation of the four miPointerSpriteFuncs that implement a hardware cursor on the same device.
Code Example 4-1 Hardware Cursor Pseudo Code

  #include "sun.h"  
  #include "dixfontstr.h"  
  #include "mipointer.h"  
  #include "cursorstr.h"  
  #include "XXXhardware.h"  
  ...  
  ...  
  static Bool  
  XXXRealizeCursor (pScreen, pCursor)  
       ScreenPtrpScreen;  
       CursorPtrpCursor;  
  {  
       pCursor->bits->devPriv[pScreen->myNum] = NULL;  
       return TRUE;  
  }  
  static Bool  
  XXXUnrealizeCursor (pScreen, pCursor)  
       ScreenPtrpScreen;  
       CursorPtrpCursor;  
  {  
       return TRUE;  
  }  

Code Example 4-1 Hardware Cursor Pseudo Code (Continued)

  /*  
   * XXXLoadCursor -- Load the cursor into XXX hardware registers. When the  
   * sunSprite layer is used, this routine is passed a cursor to install  
   * into hardware only if the cursor fits into hardware (in this case <= 32x32).  
   * However, just in case it is not the sunSprite layer calling this  
   * routine, or if for DGA reasons you decide you want to force the cursor into  
   * hardware regardless of its size, this routine is able to accept a  
   * cursor larger than 33x32, trim it around the hotspot, and fit it into the  
   * cursor register. You can either trim the cursor exactly around the  
   * hotspot (bitBlt), or trim it so that you use the  
   * 32-bit word of each scanline that the hotspot falls within. Do the latter  
   * because it is faster. (The protocol says "The components of the cursor  
   * can be transformed arbitrarily to meet display limitations...")  
   */  
  
  static void  
  XXXLoadCursor (pScreen, pCursor, x, y)  
       ScreenPtr    pScreen;  
       CursorPtr    pCursor;  
       int          x, y;  
  {  
       SetupScreen(pScreen);  
       int     w, h;  
       Unsgn32 source[32], mask[32], *pSource, *pMask;  
       int i;  
  
       w = pCursor->bits->width;  
       h = pCursor->bits->height;  
       xhot = pCursor->bits->xhot;  
       yhot = pCursor->bits->yhot;  
       /* Assumes BITMAP_SCANLINE_PAD == 32 in the non-trim case */  
       pSource = (Unsgn32 *)pCursor->bits->source;  
       pMask = (Unsgn32 *)pCursor->bits->mask;  
  
       /* Do I need to trim the cursor? */  
       if (w > 32 || h > 32) { /* trim ! */  
           int scanline = ((BitmapBytePad((int)(pCursor->bits->width))) >> 2);  
           int startWord = 0, startscan = 0, endscan = h - 1;  
           if (w > 32) {  
                xhot = pCursor->bits->xhot % 32;  
                startWord = pCursor->bits->xhot / 32;  
                w = 32;  
           }  

Code Example 4-1 Hardware Cursor Pseudo Code (Continued)

           if (h > 32) {  
                yhot = 16; /* easy to center around yhot */  
                endscan = pCursor->bits->yhot + 15;  
                while (endscan > h) {  
                endscan--;  
                yhot++;  
                }  
                startscan = endscan - 31;  
                while (startscan < 0) {  
                startscan++;  
                yhot--;  
                }  
                h = 32;  
           }  
           pSource = pSource + startWord + startscan * scanline;  
           pMask = pMask + startWord + startscan * scanline;  
           for (i = 0; i < h; i++) {  
                source[i] = *pSource; pSource += scanline;  
                mask[i] = *pMask; pMask += scanline;  
           }  
           pSource = source;  
           pMask = mask;  
       }  
  
       /* By the time we reach this point, w <= 32 && h <=32 */  
  
       /* Set the hardware cursor position and image here */  
       /* This is where hardware-specific code is added... */  
       XXXDOSETCURSORIMAGEANDPOSITION(pSource, pMask, x, y);  
  }  
  
  static void  
  XXXSetCursor (pScreen, pCursor, x, y)  
       ScreenPtr    pScreen;  
       CursorPtr    pCursor;  
       int          x, y;  
  {  
  
  if (pCursor)  
       XXXLoadCursor (pScreen, pCursor, x, y);  
  else  
       XXXDisableCursor (pScreen);  
  }  

Code Example 4-1 Hardware Cursor Pseudo Code (Continued)

  static void  
  XXXMoveCursor (pScreen, x, y)  
       ScreenPtr    pScreen;  
       int          x, y;  
  {  
       XXXMOVECURSOR((((x - xhot) << 16) | (( y - yhot) & 0xffff)));  
  }  
  
  static void  
  XXXQueryBestSize (class, pwidth, pheight, pScreen)  
       int          class;  
       short        *pwidth, *pheight;  
       ScreenPtr    pScreen;  
  {  
  
  switch (class)  
       {  
       case CursorShape:  
           if (*pwidth > 32)  
                *pwidth = 32;  
           if (*pheight > 32)  
                *pheight = 32;  
           break;  
       default:  
           mfbQueryBestSize (class, pwidth, pheight, pScreen);  
           break;  
       }  
  }  
  
  static miPointerSpriteFuncRec XXXPointerSpriteFuncs = {  
       XXXRealizeCursor,  
       XXXUnrealizeCursor,  
       XXXSetCursor,  
       XXXMoveCursor,  
  };  
  
  /*  
   * This function is called from the DDX handler's Screen Init routine. */  
  void  
  XXXCursorInitialize (pScreen)  
       ScreenPtrpScreen;  
  {  
       extern miPointerScreenFuncRec sunPointerScreenFuncs;  

Code Example 4-1 Hardware Cursor Pseudo Code (Continued)

  pScreen->QueryBestSize = XXXQueryBestSize;  
       sunSpriteInitialize (pScreen, XXXPutInHardware,  
           &XXXPointerSpriteFuncs,  
           &sunPointerScreenFuncs);  
  }  
  
  void  
  XXXDisableCursor (pScreen)  
       ScreenPtrpScreen;  
  {  
       XXXSWITCHOFFCURSOR();  
  }  

Kernel Cursor Tracking - The sunHWCursor Layer

The preceding section outlined examples of a hardware cursor implementation in which the hardware cursor was tracked by the X server process--that is, the cursor position was updated in user-domain code. Under conditions of heavy system load, this approach of tracking the cursor in the X server process might result in a considerable latency between pointer motion and corresponding cursor motion on the screen. One way to improve the interactive performance of the cursor is to track the cursor in the kernel-domain.
The sunHWCursor layer offers an implementation of a hardware cursor that is tracked in the kernel. To use this layer, the device driver for your graphics adapter must implement a set of kernel cursor tracking ioctls that are documented in Writing Device Drivers. If your device driver implements these ioctls, and you use the sunHWCursor layer utilities for your cursor implementation, a module (called hwc) is pushed on the mouse stream that intercepts mouse events and sends them directly to the graphics adapter's device driver via the Kernel Cursor Tracking ioctls issued from the kernel-domain.
Additionally, the sunHWCursor implementation is layered over the sunSprite layer. This means that when this layer is used for your cursor implementation, the cursor switches to a software form (tracked in the user-domain) over windows that define a cursor that is too large to fit in the hardware cursor image registers.
The sunHWCursor code is initialized in the DDX handler's Screen initialization function by calling the following function:

  #include "sun.h"  
  ...  
  ...  
  Bool sunCursorInitialize(ScreenPtr pScreen)  

sunCursorInitialize initializes pScreen->QueryBestSize with sunQueryBestSize, and then calls sunSpriteInitialize. As mentioned in "The sunSprite Layer" on page 30, the sunSprite layer requires an implementation of the PutInHardware, hardwareSpriteFuncs and screenFuncs functions.

Note - In this release, the ability to specialize these functions for the sunSprite layer is not available when using the sunHWCursor layer; the sunHWCursor layer has built-in implementations of these functions and the sunQueryBestSize function. The ability to specialize some of these functions when using the sunHWCursor layer might be offered in a future release of the OpenWindows server.

Invoking sunCursorInitialize in your DDX handler's initialization routine, and implementing the ioctls in the device driver is sufficient to obtain a kernel-tracked cursor. If you are in a hurry to get a kernel-tracked hardware cursor implementation going on your graphics adapter, you do not need to know all of the sunHWCursor layer details that follow.

sunHWCursor Functions

The functions provided in the sunHWCursor layer are described in this section.
sunQueryBestSize

  static void sunQueryBestSize(int class, short *pWidth,  
           short *pHeight, ScreenPtr pScreen)  

ResultsIf class is CursorShape, this function issues an ioctl to the device driver to determine the maximum hardware cursor size. For all other values of class, this function calls mfbQueryBestSize.
ReturnsIf the hardware cursor size is smaller than the maximum screen bounds, this function returns these values in pWidth and pHeight, else it returns the maximum screen bounds.
If this implementation of pScreen->QueryBestSize is not desired, supply an equivalent function in your DDX handler after sunCursorInitialize has been called.
sunPutInHardware

  static Bool sunPutInHardware(ScreenPtr pScreen,  
           CursorPtr *pCursor)  

PurposeThis function is the sunHWCursor layer's implementation of the PutInHardware routine required by the sunSprite layer.
ResultsThis function issues an ioctl to the device driver to determine the maximum hardware cursor size.
ReturnsIf the cursor passed in pCursor is larger than the hardware size, this function returns FALSE, else it returns TRUE.
screenFuncs

  extern miPointerScreenFuncRec sunPointerScreenFuncs;  

Purpose....This is an implementation of the screenFuncs functions that is passed to the sunSprite layer. See "The miPointer Layer" on page 28.
hardwareSpriteFuncs

  miPointerSpriteFuncRec sunPointerSpriteFuncs = {  
           sunRealizeCursor, sunUnRealizeCursor, sunSetCursor,  
           sunMoveCursor,  
  };  

Purpose....This is the sunHWCursor layer's implementation of the hardwareSpriteFuncs array required by the sunSprite layer. These functions load the hardware cursor, and enable kernel cursor tracking via the hwc module that has been pushed onto the mouse stream. The sunMoveCursor function is a stub that does not get called while kernel cursor tracking is active. If the cursor is switched to a software form by the sunSprite layer (this might happen when the pointer traverses a window that has a large cursor defined, which does not fit in the hardware cursor image registers), the cursor is tracked in user-domain by the miDC layer.
iv>