branch update for HEAD-2003050101
[reactos.git] / drivers / dd / vga / display / objects / pointer.c
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /* $Id$
20  *
21  * PROJECT:         ReactOS VGA16 display driver
22  * FILE:            drivers/dd/vga/display/objects/pointer.c
23  * PURPOSE:         Draws the mouse pointer.
24  */
25
26 /* INCLUDES ******************************************************************/
27
28 #include "../vgaddi.h"
29 #include "../vgavideo/vgavideo.h"
30
31 /* GLOBALS *******************************************************************/
32
33 static ULONG oldx, oldy;
34 static PSAVED_SCREEN_BITS ImageBehindCursor = NULL;
35 VOID VGADDI_HideCursor(PPDEV ppdev);
36 VOID VGADDI_ShowCursor(PPDEV ppdev);
37
38 /* FUNCTIONS *****************************************************************/
39
40 VOID
41 VGADDI_BltPointerToVGA(ULONG StartX, ULONG StartY, ULONG SizeX,
42                        ULONG SizeY, PUCHAR MaskBits, ULONG MaskOp)
43 {
44   ULONG EndX, EndY;
45   UCHAR Mask;
46   PUCHAR Video;
47   PUCHAR Src;
48   ULONG MaskPitch;
49   UCHAR SrcValue;
50   ULONG i, j;
51   ULONG Left;
52   ULONG Length;
53
54   EndX = StartX + SizeX;
55   EndY = StartY + SizeY;
56   MaskPitch = SizeX >> 3;
57
58   /* Set write mode zero. */
59   WRITE_PORT_UCHAR((PUCHAR)GRA_I, 5);
60   WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0);
61
62   /* Select raster op. */
63   WRITE_PORT_UCHAR((PUCHAR)GRA_I, 3);
64   WRITE_PORT_UCHAR((PUCHAR)GRA_D, MaskOp);
65
66   if ((StartX % 8) != 0)
67     {
68       /* Disable writes to pixels outside of the destination rectangle. */
69       Mask = (1 << (8 - (StartX % 8))) - 1;
70       if ((EndX - StartX) < (8 - (StartX % 8)))
71         {
72           Mask &= ~((1 << (8 - (EndX % 8))) - 1);
73         }
74       WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x8);
75       WRITE_PORT_UCHAR((PUCHAR)GRA_D, Mask);
76
77       /* Write the mask. */
78       Video = (PUCHAR)vidmem + StartY * 80 + (StartX >> 3);
79       Src = MaskBits + SizeY * MaskPitch;
80       for (i = 0; i < SizeY; i++, Video+=80)
81         {
82           Src -= MaskPitch;
83           SrcValue = (*Src) >> (StartX % 8);
84           (VOID)READ_REGISTER_UCHAR(Video);
85           WRITE_REGISTER_UCHAR(Video, SrcValue);
86         }
87     }
88
89   /* Enable writes to all pixels. */
90   WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x8);
91   WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0xFF);
92
93   /* Have we finished. */
94   if ((EndX - StartX) < (8 - (StartX % 8)))
95     {
96       return;
97     }
98
99   /* Fill any whole rows of eight pixels. */
100   Left = (StartX + 7) & ~0x7;
101   Length = (EndX >> 3) - (Left >> 3);
102   for (i = StartY; i < EndY; i++)
103     {
104       Video = (PUCHAR)vidmem + i * 80 + (Left >> 3);
105       Src = MaskBits + (EndY - i - 1) * MaskPitch;
106       for (j = 0; j < Length; j++, Video++, Src++)
107         {
108           if ((StartX % 8) != 0)
109             {
110               SrcValue = (Src[0] << (8 - (StartX % 8)));
111               SrcValue |= (Src[1] >> (StartX % 8));
112             }
113           else
114             {
115               SrcValue = Src[0];
116             }
117           (VOID)READ_REGISTER_UCHAR(Video);
118           WRITE_REGISTER_UCHAR(Video, SrcValue);
119         }
120     }
121
122   /* Fill any pixels on the right which don't fall into a complete row. */
123   if ((EndX % 8) != 0)
124     {
125       /* Disable writes to pixels outside the destination rectangle. */
126       Mask = ~((1 << (8 - (EndX % 8))) - 1);
127       WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x8);
128       WRITE_PORT_UCHAR((PUCHAR)GRA_D, Mask);
129
130       Video = (PUCHAR)vidmem + StartY * 80 + (EndX >> 3);
131       Src = MaskBits + SizeY * MaskPitch + (SizeX >> 3) - 1;
132       for (i = StartY; i < EndY; i++, Video+=80)
133         {
134           Src -= MaskPitch;
135           SrcValue = (Src[0] << (8 - (StartX % 8)));
136           (VOID)READ_REGISTER_UCHAR(Video);
137           WRITE_REGISTER_UCHAR(Video, SrcValue);
138         }
139
140       /* Restore the default write masks. */
141       WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x8);
142       WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0xFF);
143     }
144
145   /* Set write mode two. */
146   WRITE_PORT_UCHAR((PUCHAR)GRA_I, 5);
147   WRITE_PORT_UCHAR((PUCHAR)GRA_D, 2);
148
149   /* Select raster op replace. */
150   WRITE_PORT_UCHAR((PUCHAR)GRA_I, 3);
151   WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0);
152 }
153
154 BOOL InitPointer(PPDEV ppdev)
155 {
156   ULONG CursorWidth = 32, CursorHeight = 32;
157   ULONG PointerAttributesSize;
158   ULONG SavedMemSize;
159
160   /* Determine the size of the pointer attributes */
161   PointerAttributesSize = sizeof(VIDEO_POINTER_ATTRIBUTES) +
162     ((CursorWidth * CursorHeight * 2) >> 3);
163
164   /* Allocate memory for pointer attributes */
165   ppdev->pPointerAttributes = EngAllocMem(0, PointerAttributesSize, ALLOC_TAG);
166
167   ppdev->pPointerAttributes->Flags = 0; /* FIXME: Do this right */
168   ppdev->pPointerAttributes->Width = CursorWidth;
169   ppdev->pPointerAttributes->Height = CursorHeight;
170   ppdev->pPointerAttributes->WidthInBytes = CursorWidth >> 3;
171   ppdev->pPointerAttributes->Enable = 0;
172   ppdev->pPointerAttributes->Column = 0;
173   ppdev->pPointerAttributes->Row = 0;
174
175   /* Allocate memory for the pixels behind the cursor */
176   SavedMemSize = ((((CursorWidth + 7) & ~0x7) + 16) * CursorHeight) >> 3;
177   ImageBehindCursor = VGADDI_AllocSavedScreenBits(SavedMemSize);
178
179   return(TRUE);
180 }
181
182
183 VOID STDCALL
184 DrvMovePointer(IN PSURFOBJ pso,
185                IN LONG x,
186                IN LONG y,
187                IN PRECTL prcl)
188 {
189   PPDEV ppdev = (PPDEV)pso->dhpdev;
190
191   if (x < 0 )
192     {
193       /* x < 0 and y < 0 indicates we must hide the cursor */
194       VGADDI_HideCursor(ppdev);
195       return;
196     }
197
198   ppdev->xyCursor.x = x;
199   ppdev->xyCursor.y = y;
200
201   VGADDI_ShowCursor(ppdev);
202
203   /* Give feedback on the new cursor rectangle */
204   /*if (prcl != NULL) ComputePointerRect(ppdev, prcl);*/
205 }
206
207
208 ULONG STDCALL
209 DrvSetPointerShape(PSURFOBJ pso,
210                    PSURFOBJ psoMask,
211                    PSURFOBJ psoColor,
212                    PXLATEOBJ pxlo,
213                    LONG xHot,
214                    LONG yHot,
215                    LONG x,
216                    LONG y,
217                    PRECTL prcl,
218                    ULONG fl)
219 {
220   PPDEV ppdev = (PPDEV)pso->dhpdev;
221   ULONG NewWidth, NewHeight;
222   PUCHAR Src, Dest;
223   ULONG i, j;
224
225   NewWidth = psoMask->lDelta << 3;
226   NewHeight = (psoMask->cjBits / psoMask->lDelta) / 2;
227
228   /* Hide the cursor */
229   if(ppdev->pPointerAttributes->Enable != 0)
230     {
231       VGADDI_HideCursor(ppdev);
232     }
233
234   /* Reallocate the space for the cursor if necessary. */
235   if (ppdev->pPointerAttributes->Width != NewWidth ||
236       ppdev->pPointerAttributes->Height != NewHeight)
237     {
238       ULONG PointerAttributesSize;
239       PVIDEO_POINTER_ATTRIBUTES NewPointerAttributes;
240       ULONG SavedMemSize;
241
242       /* Determine the size of the pointer attributes */
243       PointerAttributesSize = sizeof(VIDEO_POINTER_ATTRIBUTES) +
244         ((NewWidth * NewHeight * 2) >> 3);
245
246       /* Allocate memory for pointer attributes */
247       NewPointerAttributes = EngAllocMem(0, PointerAttributesSize, ALLOC_TAG);
248       *NewPointerAttributes = *ppdev->pPointerAttributes;
249       NewPointerAttributes->Width = NewWidth;
250       NewPointerAttributes->Height = NewHeight;
251       NewPointerAttributes->WidthInBytes = NewWidth >> 3;
252       EngFreeMem(ppdev->pPointerAttributes);
253       ppdev->pPointerAttributes = NewPointerAttributes;
254
255       /* Reallocate the space for the saved bits. */
256       VGADDI_FreeSavedScreenBits(ImageBehindCursor);
257       SavedMemSize = ((((NewWidth + 7) & ~0x7) + 16) * NewHeight) >> 3;
258       ImageBehindCursor = VGADDI_AllocSavedScreenBits(SavedMemSize);
259     }
260
261   /* Copy the new cursor in. */
262   for (i = 0; i < (NewHeight * 2); i++)
263     {
264       Src = (PUCHAR)psoMask->pvBits;
265       Src += (i * (NewWidth >> 3));
266       Dest = (PUCHAR)ppdev->pPointerAttributes->Pixels;
267       if (i >= NewHeight)
268         {
269           Dest += (((NewHeight * 3) - i - 1) * (NewWidth >> 3));
270         }
271       else
272         {
273           Dest += ((NewHeight - i - 1) * (NewWidth >> 3));
274         }
275       memcpy(Dest, Src, NewWidth >> 3);
276     }
277
278   /* Set the new cursor position */
279   ppdev->xyCursor.x = x;
280   ppdev->xyCursor.y = y;
281
282   /* Show the cursor */
283   VGADDI_ShowCursor(ppdev);
284
285   return SPS_ACCEPT_EXCLUDE;
286 }
287
288 VOID
289 VGADDI_HideCursor(PPDEV ppdev)
290 {
291   ULONG i, j, cx, cy, bitpos;
292   ULONG SizeX;
293
294   /* Display what was behind cursor */
295   SizeX = ((oldx + ppdev->pPointerAttributes->Width) + 7) & ~0x7;
296   SizeX -= (oldx & ~0x7);
297   VGADDI_BltFromSavedScreenBits(oldx & ~0x7,
298                                 oldy,
299                                 ImageBehindCursor,
300                                 SizeX,
301                                 ppdev->pPointerAttributes->Height);
302
303   ppdev->pPointerAttributes->Enable = 0;
304 }
305
306 VOID
307 VGADDI_ShowCursor(PPDEV ppdev)
308 {
309   ULONG i, j, cx, cy;
310   PUCHAR XorMask;
311   ULONG SizeX;
312
313   if (ppdev->pPointerAttributes->Enable != 0)
314     {
315       VGADDI_HideCursor(ppdev);
316     }
317
318   /* Capture pixels behind the cursor */
319   cx = ppdev->xyCursor.x;
320   cy = ppdev->xyCursor.y;
321
322   /* Used to repaint background */
323   SizeX = ((cx + ppdev->pPointerAttributes->Width) + 7) & ~0x7;
324   SizeX -= (cx & ~0x7);
325
326   VGADDI_BltToSavedScreenBits(ImageBehindCursor,
327                               cx & ~0x7,
328                               cy,
329                               SizeX,
330                               ppdev->pPointerAttributes->Height);
331
332   /* Display the cursor. */
333   XorMask = ppdev->pPointerAttributes->Pixels +
334     ppdev->pPointerAttributes->WidthInBytes *
335     ppdev->pPointerAttributes->Height;
336   VGADDI_BltPointerToVGA(ppdev->xyCursor.x,
337                          ppdev->xyCursor.y,
338                          ppdev->pPointerAttributes->Width,
339                          ppdev->pPointerAttributes->Height,
340                          ppdev->pPointerAttributes->Pixels,
341                          VGA_AND);
342   VGADDI_BltPointerToVGA(ppdev->xyCursor.x,
343                          ppdev->xyCursor.y,
344                          ppdev->pPointerAttributes->Width,
345                          ppdev->pPointerAttributes->Height,
346                          XorMask,
347                          VGA_XOR);
348
349   /* Save the new cursor location. */
350   oldx = ppdev->xyCursor.x;
351   oldy = ppdev->xyCursor.y;
352
353   /* Mark the cursor as currently displayed. */
354   ppdev->pPointerAttributes->Enable = 1;
355 }