c5194f2771fcb0b694b6ab195bae01e0de70ffdf
[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 MaskPitch, ULONG MaskOp)
43 {
44   ULONG EndX, EndY;
45   UCHAR Mask;
46   PUCHAR Video;
47   PUCHAR Src;
48   UCHAR SrcValue;
49   ULONG i, j;
50   ULONG Left;
51   ULONG Length;
52
53   EndX = StartX + SizeX;
54   EndY = StartY + SizeY;
55
56   /* Set write mode zero. */
57   WRITE_PORT_UCHAR((PUCHAR)GRA_I, 5);
58   WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0);
59
60   /* Select raster op. */
61   WRITE_PORT_UCHAR((PUCHAR)GRA_I, 3);
62   WRITE_PORT_UCHAR((PUCHAR)GRA_D, MaskOp);
63
64   if ((StartX % 8) != 0)
65     {
66       /* Disable writes to pixels outside of the destination rectangle. */
67       Mask = (1 << (8 - (StartX % 8))) - 1;
68       if ((EndX - StartX) < (8 - (StartX % 8)))
69         {
70           Mask &= ~((1 << (8 - (EndX % 8))) - 1);
71         }
72       WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x8);
73       WRITE_PORT_UCHAR((PUCHAR)GRA_D, Mask);
74
75       /* Write the mask. */
76       Video = (PUCHAR)vidmem + StartY * 80 + (StartX >> 3);
77       Src = MaskBits + SizeY * MaskPitch;
78       for (i = 0; i < SizeY; i++, Video+=80)
79         {
80           Src -= MaskPitch;
81           SrcValue = (*Src) >> (StartX % 8);
82           (VOID)READ_REGISTER_UCHAR(Video);
83           WRITE_REGISTER_UCHAR(Video, SrcValue);
84         }
85     }
86
87   /* Enable writes to all pixels. */
88   WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x8);
89   WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0xFF);
90
91   /* Have we finished. */
92   if ((EndX - StartX) < (8 - (StartX % 8)))
93     {
94       return;
95     }
96
97   /* Fill any whole rows of eight pixels. */
98   Left = (StartX + 7) & ~0x7;
99   Length = (EndX >> 3) - (Left >> 3);
100   for (i = StartY; i < EndY; i++)
101     {
102       Video = (PUCHAR)vidmem + i * 80 + (Left >> 3);
103       Src = MaskBits + (EndY - i - 1) * MaskPitch;
104       for (j = 0; j < Length; j++, Video++, Src++)
105         {
106           if ((StartX % 8) != 0)
107             {
108               SrcValue = (Src[0] << (8 - (StartX % 8)));
109               SrcValue |= (Src[1] >> (StartX % 8));
110             }
111           else
112             {
113               SrcValue = Src[0];
114             }
115           (VOID)READ_REGISTER_UCHAR(Video);
116           WRITE_REGISTER_UCHAR(Video, SrcValue);
117         }
118     }
119
120   /* Fill any pixels on the right which don't fall into a complete row. */
121   if ((EndX % 8) != 0)
122     {
123       /* Disable writes to pixels outside the destination rectangle. */
124       Mask = ~((1 << (8 - (EndX % 8))) - 1);
125       WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x8);
126       WRITE_PORT_UCHAR((PUCHAR)GRA_D, Mask);
127
128       Video = (PUCHAR)vidmem + StartY * 80 + (EndX >> 3);
129       Src = MaskBits + SizeY * MaskPitch + (SizeX >> 3) - 1;
130       for (i = StartY; i < EndY; i++, Video+=80)
131         {
132           Src -= MaskPitch;
133           SrcValue = (Src[0] << (8 - (StartX % 8)));
134           (VOID)READ_REGISTER_UCHAR(Video);
135           WRITE_REGISTER_UCHAR(Video, SrcValue);
136         }
137
138       /* Restore the default write masks. */
139       WRITE_PORT_UCHAR((PUCHAR)GRA_I, 0x8);
140       WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0xFF);
141     }
142
143   /* Set write mode two. */
144   WRITE_PORT_UCHAR((PUCHAR)GRA_I, 5);
145   WRITE_PORT_UCHAR((PUCHAR)GRA_D, 2);
146
147   /* Select raster op replace. */
148   WRITE_PORT_UCHAR((PUCHAR)GRA_I, 3);
149   WRITE_PORT_UCHAR((PUCHAR)GRA_D, 0);
150 }
151
152 BOOL InitPointer(PPDEV ppdev)
153 {
154   ULONG CursorWidth = 32, CursorHeight = 32;
155   ULONG PointerAttributesSize;
156   ULONG SavedMemSize;
157
158   /* Determine the size of the pointer attributes */
159   PointerAttributesSize = sizeof(VIDEO_POINTER_ATTRIBUTES) +
160     ((CursorWidth * CursorHeight * 2) >> 3);
161
162   /* Allocate memory for pointer attributes */
163   ppdev->pPointerAttributes = EngAllocMem(0, PointerAttributesSize, ALLOC_TAG);
164
165   ppdev->pPointerAttributes->Flags = 0; /* FIXME: Do this right */
166   ppdev->pPointerAttributes->Width = CursorWidth;
167   ppdev->pPointerAttributes->Height = CursorHeight;
168   ppdev->pPointerAttributes->WidthInBytes = CursorWidth >> 3;
169   ppdev->pPointerAttributes->Enable = 0;
170   ppdev->pPointerAttributes->Column = 0;
171   ppdev->pPointerAttributes->Row = 0;
172
173   /* Allocate memory for the pixels behind the cursor */
174   SavedMemSize = ((((CursorWidth + 7) & ~0x7) + 16) * CursorHeight) >> 3;
175   ImageBehindCursor = VGADDI_AllocSavedScreenBits(SavedMemSize);
176
177   return(TRUE);
178 }
179
180
181 VOID STDCALL
182 DrvMovePointer(IN PSURFOBJ pso,
183                IN LONG x,
184                IN LONG y,
185                IN PRECTL prcl)
186 {
187   PPDEV ppdev = (PPDEV)pso->dhpdev;
188
189   if (x < 0 )
190     {
191       /* x < 0 and y < 0 indicates we must hide the cursor */
192       VGADDI_HideCursor(ppdev);
193       return;
194     }
195
196   ppdev->xyCursor.x = x;
197   ppdev->xyCursor.y = y;
198
199   VGADDI_ShowCursor(ppdev);
200
201   /* Give feedback on the new cursor rectangle */
202   /*if (prcl != NULL) ComputePointerRect(ppdev, prcl);*/
203 }
204
205
206 ULONG STDCALL
207 DrvSetPointerShape(PSURFOBJ pso,
208                    PSURFOBJ psoMask,
209                    PSURFOBJ psoColor,
210                    PXLATEOBJ pxlo,
211                    LONG xHot,
212                    LONG yHot,
213                    LONG x,
214                    LONG y,
215                    PRECTL prcl,
216                    ULONG fl)
217 {
218   PPDEV ppdev = (PPDEV)pso->dhpdev;
219   ULONG NewWidth, NewHeight;
220   PUCHAR Src, Dest;
221   ULONG i, j;
222
223   NewWidth = psoMask->lDelta << 3;
224   NewHeight = (psoMask->cjBits / psoMask->lDelta) / 2;
225
226   /* Hide the cursor */
227   if(ppdev->pPointerAttributes->Enable != 0)
228     {
229       VGADDI_HideCursor(ppdev);
230     }
231
232   /* Reallocate the space for the cursor if necessary. */
233   if (ppdev->pPointerAttributes->Width != NewWidth ||
234       ppdev->pPointerAttributes->Height != NewHeight)
235     {
236       ULONG PointerAttributesSize;
237       PVIDEO_POINTER_ATTRIBUTES NewPointerAttributes;
238       ULONG SavedMemSize;
239
240       /* Determine the size of the pointer attributes */
241       PointerAttributesSize = sizeof(VIDEO_POINTER_ATTRIBUTES) +
242         ((NewWidth * NewHeight * 2) >> 3);
243
244       /* Allocate memory for pointer attributes */
245       NewPointerAttributes = EngAllocMem(0, PointerAttributesSize, ALLOC_TAG);
246       *NewPointerAttributes = *ppdev->pPointerAttributes;
247       NewPointerAttributes->Width = NewWidth;
248       NewPointerAttributes->Height = NewHeight;
249       NewPointerAttributes->WidthInBytes = NewWidth >> 3;
250       EngFreeMem(ppdev->pPointerAttributes);
251       ppdev->pPointerAttributes = NewPointerAttributes;
252
253       /* Reallocate the space for the saved bits. */
254       VGADDI_FreeSavedScreenBits(ImageBehindCursor);
255       SavedMemSize = ((((NewWidth + 7) & ~0x7) + 16) * NewHeight) >> 3;
256       ImageBehindCursor = VGADDI_AllocSavedScreenBits(SavedMemSize);
257     }
258
259   /* Copy the new cursor in. */
260   for (i = 0; i < (NewHeight * 2); i++)
261     {
262       Src = (PUCHAR)psoMask->pvBits;
263       Src += (i * (NewWidth >> 3));
264       Dest = (PUCHAR)ppdev->pPointerAttributes->Pixels;
265       if (i >= NewHeight)
266         {
267           Dest += (((NewHeight * 3) - i - 1) * (NewWidth >> 3));
268         }
269       else
270         {
271           Dest += ((NewHeight - i - 1) * (NewWidth >> 3));
272         }
273       memcpy(Dest, Src, NewWidth >> 3);
274     }
275
276   /* Set the new cursor position */
277   ppdev->xyCursor.x = x;
278   ppdev->xyCursor.y = y;
279
280   /* Show the cursor */
281   VGADDI_ShowCursor(ppdev);
282
283   return SPS_ACCEPT_EXCLUDE;
284 }
285
286 VOID
287 VGADDI_HideCursor(PPDEV ppdev)
288 {
289   ULONG i, j, cx, cy, bitpos;
290   ULONG SizeX, SizeY;
291
292   /* Display what was behind cursor */
293   SizeX = min(((oldx + ppdev->pPointerAttributes->Width) + 7) & ~0x7, ppdev->sizeSurf.cx);
294   SizeX -= (oldx & ~0x7);
295   SizeY = min(ppdev->pPointerAttributes->Height, ppdev->sizeSurf.cy - oldy);
296   VGADDI_BltFromSavedScreenBits(oldx & ~0x7,
297                                 oldy,
298                                 ImageBehindCursor,
299                                 SizeX,
300                                 SizeY);
301
302   ppdev->pPointerAttributes->Enable = 0;
303 }
304
305 VOID
306 VGADDI_ShowCursor(PPDEV ppdev)
307 {
308   ULONG i, j, cx, cy;
309   PUCHAR AndMask, XorMask;
310   ULONG SizeX, SizeY;
311
312   if (ppdev->pPointerAttributes->Enable != 0)
313     {
314       VGADDI_HideCursor(ppdev);
315     }
316
317   /* Capture pixels behind the cursor */
318   cx = ppdev->xyCursor.x;
319   cy = ppdev->xyCursor.y;
320
321   /* Used to repaint background */
322   SizeX = min(((cx + ppdev->pPointerAttributes->Width) + 7) & ~0x7, ppdev->sizeSurf.cx);
323   SizeX -= (cx & ~0x7);
324   SizeY = min(ppdev->pPointerAttributes->Height, ppdev->sizeSurf.cy - cy);
325
326   VGADDI_BltToSavedScreenBits(ImageBehindCursor,
327                               cx & ~0x7,
328                               cy,
329                               SizeX,
330                               SizeY);
331
332   /* Display the cursor. */
333   SizeX = min(ppdev->pPointerAttributes->Width, ppdev->sizeSurf.cx - ppdev->xyCursor.x);
334   SizeY = min(ppdev->pPointerAttributes->Height, ppdev->sizeSurf.cy - ppdev->xyCursor.y);
335   AndMask = ppdev->pPointerAttributes->Pixels +
336             (ppdev->pPointerAttributes->Height - SizeY) * ppdev->pPointerAttributes->WidthInBytes;
337   VGADDI_BltPointerToVGA(ppdev->xyCursor.x,
338                          ppdev->xyCursor.y,
339                          SizeX,
340                          SizeY,
341                          AndMask,
342                          ppdev->pPointerAttributes->WidthInBytes,
343                          VGA_AND);
344   XorMask = AndMask +
345     ppdev->pPointerAttributes->WidthInBytes *
346     ppdev->pPointerAttributes->Height;
347   VGADDI_BltPointerToVGA(ppdev->xyCursor.x,
348                          ppdev->xyCursor.y,
349                          SizeX,
350                          SizeY,
351                          XorMask,
352                          ppdev->pPointerAttributes->WidthInBytes,
353                          VGA_XOR);
354
355   /* Save the new cursor location. */
356   oldx = ppdev->xyCursor.x;
357   oldy = ppdev->xyCursor.y;
358
359   /* Mark the cursor as currently displayed. */
360   ppdev->pPointerAttributes->Enable = 1;
361 }