1 /*
2  * PROJECT:         ReactOS VGA display driver
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            win32ss/drivers/displays/vga/objects/pointer.c
5  * PURPOSE:         Draws the mouse pointer
6  * PROGRAMMERS:     Copyright (C) 1998-2001 ReactOS Team
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <vgaddi.h>
12 
13 /* GLOBALS *******************************************************************/
14 
15 static VOID VGADDI_HideCursor(PPDEV ppdev);
16 static VOID VGADDI_ShowCursor(PPDEV ppdev, PRECTL prcl);
17 
18 /* FUNCTIONS *****************************************************************/
19 
20 VOID
21 VGADDI_BltPointerToVGA(
22     IN LONG StartX,
23     IN LONG StartY,
24     IN ULONG SizeX,
25     IN ULONG SizeY,
26     IN PUCHAR MaskBits,
27     IN ULONG MaskPitch,
28     IN ULONG MaskOp)
29 {
30     ULONG DestX, EndX, DestY, EndY;
31     UCHAR Mask;
32     PUCHAR Video;
33     PUCHAR Src;
34     UCHAR SrcValue;
35     ULONG i, j;
36     ULONG Left;
37     ULONG Length;
38     LONG Bits;
39 
40     DestX = StartX < 0 ? 0 : StartX;
41     DestY = StartY < 0 ? 0 : StartY;
42     EndX = StartX + SizeX;
43     EndY = StartY + SizeY;
44 
45     /* Set write mode zero. */
46     WRITE_PORT_UCHAR((PUCHAR)GRA_I, 5);
47     WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0);
48 
49     /* Select raster op. */
50     WRITE_PORT_UCHAR((PUCHAR)GRA_I, 3);
51     WRITE_PORT_UCHAR((PUCHAR)GRA_D, MaskOp);
52 
53     if ((DestX % 8) != 0)
54     {
55         /* Disable writes to pixels outside of the destination rectangle. */
56         Mask = (1 << (8 - (DestX % 8))) - 1;
57         if ((EndX - DestX) < (8 - (DestX % 8)))
58         {
59             Mask &= ~((1 << (8 - (EndX % 8))) - 1);
60         }
61         WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x8);
62         WRITE_PORT_UCHAR((PUCHAR)GRA_D, Mask);
63 
64         /* Write the mask. */
65         Video = (PUCHAR)vidmem + DestY * 80 + (DestX >> 3);
66         Src = MaskBits + (SizeY - (DestY - StartY)) * MaskPitch;
67         for (i = DestY; i < EndY; i++, Video += 80)
68         {
69             Src -= MaskPitch;
70             SrcValue = (*Src) >> (DestX % 8);
71             (VOID)READ_REGISTER_UCHAR(Video);
72             WRITE_REGISTER_UCHAR(Video, SrcValue);
73         }
74     }
75 
76     /* Enable writes to all pixels. */
77     WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x8);
78     WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0xFF);
79 
80     /* Have we finished. */
81     if ((EndX - DestX) < (8 - (DestX % 8)))
82         return;
83 
84     /* Fill any whole rows of eight pixels. */
85     Left = (DestX + 7) & ~0x7;
86     Length = (EndX >> 3) - (Left >> 3);
87     Bits = StartX;
88     while (Bits < 0)
89         Bits += 8;
90     Bits = Bits % 8;
91     for (i = DestY; i < EndY; i++)
92     {
93         Video = (PUCHAR)vidmem + i * 80 + (Left >> 3);
94         Src = MaskBits + (EndY - i - 1) * MaskPitch + ((DestX - StartX) >> 3);
95         for (j = 0; j < Length; j++, Video++, Src++)
96         {
97             if (Bits != 0)
98             {
99                 SrcValue = (Src[0] << (8 - Bits));
100                 SrcValue |= (Src[1] >> Bits);
101             }
102             else
103             {
104                 SrcValue = Src[0];
105             }
106             (VOID)READ_REGISTER_UCHAR(Video);
107             WRITE_REGISTER_UCHAR(Video, SrcValue);
108         }
109     }
110 
111     /* Fill any pixels on the right which don't fall into a complete row. */
112     if ((EndX % 8) != 0)
113     {
114         /* Disable writes to pixels outside the destination rectangle. */
115         Mask = ~((1 << (8 - (EndX % 8))) - 1);
116         WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x8);
117         WRITE_PORT_UCHAR((PUCHAR)GRA_D, Mask);
118 
119         Video = (PUCHAR)vidmem + DestY * 80 + (EndX >> 3);
120         Src = MaskBits + (SizeY - (DestY - StartY)) * MaskPitch + (SizeX >> 3) - 1;
121         for (i = DestY; i < EndY; i++, Video += 80)
122         {
123             Src -= MaskPitch;
124             SrcValue = (Src[0] << (8 - Bits));
125             (VOID)READ_REGISTER_UCHAR(Video);
126             WRITE_REGISTER_UCHAR(Video, SrcValue);
127         }
128 
129         /* Restore the default write masks. */
130         WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x8);
131         WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0xFF);
132     }
133 
134     /* Set write mode two. */
135     WRITE_PORT_UCHAR((PUCHAR)GRA_I, 5);
136     WRITE_PORT_UCHAR((PUCHAR)GRA_D, 2);
137 
138     /* Select raster op replace. */
139     WRITE_PORT_UCHAR((PUCHAR)GRA_I, 3);
140     WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0);
141 }
142 
143 BOOL InitPointer(PPDEV ppdev)
144 {
145     ULONG CursorWidth = 32, CursorHeight = 32;
146     ULONG PointerAttributesSize;
147     ULONG SavedMemSize;
148 
149     ppdev->xyHotSpot.x = 0;
150     ppdev->xyHotSpot.y = 0;
151 
152     /* Determine the size of the pointer attributes */
153     PointerAttributesSize = sizeof(VIDEO_POINTER_ATTRIBUTES) +
154       ((CursorWidth * CursorHeight * 2) >> 3);
155 
156     /* Allocate memory for pointer attributes */
157     ppdev->pPointerAttributes = EngAllocMem(0, PointerAttributesSize, ALLOC_TAG);
158 
159     ppdev->pPointerAttributes->Flags = 0; /* FIXME: Do this right */
160     ppdev->pPointerAttributes->Width = CursorWidth;
161     ppdev->pPointerAttributes->Height = CursorHeight;
162     ppdev->pPointerAttributes->WidthInBytes = CursorWidth >> 3;
163     ppdev->pPointerAttributes->Enable = 0;
164     ppdev->pPointerAttributes->Column = 0;
165     ppdev->pPointerAttributes->Row = 0;
166 
167     /* Allocate memory for the pixels behind the cursor */
168     SavedMemSize = ((((CursorWidth + 7) & ~0x7) + 16) * CursorHeight) >> 3;
169     ppdev->ImageBehindCursor = VGADDI_AllocSavedScreenBits(SavedMemSize);
170 
171     return TRUE;
172 }
173 
174 VOID APIENTRY
175 DrvMovePointer(
176     IN SURFOBJ* pso,
177     IN LONG x,
178     IN LONG y,
179     IN PRECTL prcl)
180 {
181     PPDEV ppdev = (PPDEV)pso->dhpdev;
182 
183     VGADDI_HideCursor(ppdev);
184 
185     if(x != -1)
186     {
187         ppdev->pPointerAttributes->Column = x;
188         ppdev->pPointerAttributes->Row = y;
189 
190         VGADDI_ShowCursor(ppdev, prcl);
191     }
192 }
193 
194 
195 ULONG APIENTRY
196 DrvSetPointerShape(
197     IN SURFOBJ* pso,
198     IN SURFOBJ* psoMask,
199     IN SURFOBJ* psoColor,
200     IN XLATEOBJ* pxlo,
201     IN LONG xHot,
202     IN LONG yHot,
203     IN LONG x,
204     IN LONG y,
205     IN PRECTL prcl,
206     IN ULONG fl)
207 {
208     PPDEV ppdev = (PPDEV)pso->dhpdev;
209     ULONG NewWidth, NewHeight;
210     PUCHAR Src, Dest;
211     ULONG i;
212 
213     if (!psoMask)
214         return SPS_DECLINE;
215 
216     /* Hide the cursor */
217     VGADDI_HideCursor(ppdev);
218 
219     NewWidth = abs(psoMask->lDelta) << 3;
220     NewHeight = (psoMask->cjBits / abs(psoMask->lDelta)) / 2;
221 
222     /* Reallocate the space for the cursor if necessary. */
223     if (ppdev->pPointerAttributes->Width != NewWidth ||
224         ppdev->pPointerAttributes->Height != NewHeight)
225     {
226         ULONG PointerAttributesSize;
227         PVIDEO_POINTER_ATTRIBUTES NewPointerAttributes;
228         ULONG SavedMemSize;
229 
230         /* Determine the size of the pointer attributes */
231         PointerAttributesSize = sizeof(VIDEO_POINTER_ATTRIBUTES) +
232             ((NewWidth * NewHeight * 2) >> 3);
233 
234         /* Allocate memory for pointer attributes */
235         NewPointerAttributes = EngAllocMem(0, PointerAttributesSize, ALLOC_TAG);
236         *NewPointerAttributes = *ppdev->pPointerAttributes;
237         NewPointerAttributes->Width = NewWidth;
238         NewPointerAttributes->Height = NewHeight;
239         NewPointerAttributes->WidthInBytes = NewWidth >> 3;
240         EngFreeMem(ppdev->pPointerAttributes);
241         ppdev->pPointerAttributes = NewPointerAttributes;
242 
243         /* Reallocate the space for the saved bits. */
244         VGADDI_FreeSavedScreenBits(ppdev->ImageBehindCursor);
245         SavedMemSize = ((((NewWidth + 7) & ~0x7) + 16) * NewHeight) >> 3;
246         ppdev->ImageBehindCursor = VGADDI_AllocSavedScreenBits(SavedMemSize);
247     }
248 
249     Src = (PUCHAR)psoMask->pvScan0;
250     /* Copy the new cursor in. */
251     for (i = 0; i < (NewHeight * 2); i++)
252     {
253         Dest = (PUCHAR)ppdev->pPointerAttributes->Pixels;
254         if (i >= NewHeight)
255             Dest += (((NewHeight * 3) - i - 1) * (NewWidth >> 3));
256         else
257             Dest += ((NewHeight - i - 1) * (NewWidth >> 3));
258         memcpy(Dest, Src, NewWidth >> 3);
259         Src += psoMask->lDelta;
260     }
261 
262     /* Set the new cursor position */
263     ppdev->xyHotSpot.x = xHot;
264     ppdev->xyHotSpot.y = yHot;
265 
266     if(x != -1)
267     {
268         ppdev->pPointerAttributes->Column = x;
269         ppdev->pPointerAttributes->Row = y;
270 
271       /* show the cursor */
272       VGADDI_ShowCursor(ppdev, prcl);
273     }
274 
275     return SPS_ACCEPT_NOEXCLUDE;
276 }
277 
278 static VOID FASTCALL
279 VGADDI_ComputePointerRect(
280     IN PPDEV ppdev,
281     IN LONG X,
282     IN LONG Y,
283     IN PRECTL Rect)
284 {
285     ULONG SizeX, SizeY;
286 
287     SizeX = min(((X + (LONG)ppdev->pPointerAttributes->Width) + 7) & ~0x7, ppdev->sizeSurf.cx);
288     SizeX -= (X & ~0x7);
289     SizeY = min((LONG)ppdev->pPointerAttributes->Height, ppdev->sizeSurf.cy - Y);
290 
291     Rect->left = max(X, 0) & ~0x7;
292     Rect->top = max(Y, 0);
293     Rect->right = Rect->left + SizeX;
294     Rect->bottom = Rect->top + SizeY;
295 }
296 
297 static VOID
298 VGADDI_HideCursor(PPDEV ppdev)
299 {
300     if(ppdev->pPointerAttributes->Enable)
301     {
302         LONG cx, cy;
303         RECTL Rect;
304 
305         ppdev->pPointerAttributes->Enable = 0;
306 
307         cx = ppdev->pPointerAttributes->Column - ppdev->xyHotSpot.x;
308         cy = ppdev->pPointerAttributes->Row - ppdev->xyHotSpot.y;
309 
310         VGADDI_ComputePointerRect(ppdev, cx, cy, &Rect);
311 
312         /* Display what was behind cursor */
313         VGADDI_BltFromSavedScreenBits(Rect.left,
314                                       Rect.top,
315                                       ppdev->ImageBehindCursor,
316                                       Rect.right - Rect.left,
317                                      Rect.bottom - Rect.top);
318     }
319 }
320 
321 static VOID
322 VGADDI_ShowCursor(PPDEV ppdev, PRECTL prcl)
323 {
324     LONG cx, cy;
325     PUCHAR AndMask, XorMask;
326     ULONG SizeX, SizeY;
327     RECTL Rect;
328 
329     if(ppdev->pPointerAttributes->Enable)
330         return;
331 
332     /* Mark the cursor as currently displayed. */
333     ppdev->pPointerAttributes->Enable = 1;
334 
335     cx = ppdev->pPointerAttributes->Column - ppdev->xyHotSpot.x;
336     cy = ppdev->pPointerAttributes->Row - ppdev->xyHotSpot.y;
337 
338     /* Capture pixels behind the cursor */
339     VGADDI_ComputePointerRect(ppdev, cx, cy, &Rect);
340 
341     VGADDI_BltToSavedScreenBits(ppdev->ImageBehindCursor,
342                                 Rect.left,
343                                 Rect.top,
344                                 Rect.right - Rect.left,
345                                 Rect.bottom - Rect.top);
346 
347     /* Display the cursor. */
348     SizeX = min((LONG)ppdev->pPointerAttributes->Width, ppdev->sizeSurf.cx - cx);
349     SizeY = min((LONG)ppdev->pPointerAttributes->Height, ppdev->sizeSurf.cy - cy);
350     AndMask = ppdev->pPointerAttributes->Pixels +
351               (ppdev->pPointerAttributes->Height - SizeY) * ppdev->pPointerAttributes->WidthInBytes;
352     VGADDI_BltPointerToVGA(cx,
353                            cy,
354                            SizeX,
355                            SizeY,
356                            AndMask,
357                            ppdev->pPointerAttributes->WidthInBytes,
358                            VGA_AND);
359     XorMask = AndMask +
360         ppdev->pPointerAttributes->WidthInBytes *
361         ppdev->pPointerAttributes->Height;
362     VGADDI_BltPointerToVGA(cx,
363                            cy,
364                            SizeX,
365                            SizeY,
366                            XorMask,
367                            ppdev->pPointerAttributes->WidthInBytes,
368                            VGA_XOR);
369 
370     if (NULL != prcl)
371         *prcl = Rect;
372 }
373