|
|
1.1 root 1: /******************************Module*Header*******************************\
2: * Module Name: pointer.c
3: *
4: * This module contains the hardware cursor support for Disp.
5: *
6: * Copyright (c) 1992 Microsoft Corporation
7: \**************************************************************************/
8:
9: #include "driver.h"
10:
11:
12: ULONG DrvSetColorPointerShape(
13: SURFOBJ *pso,
14: SURFOBJ *psoMask,
15: SURFOBJ *psoColor,
16: XLATEOBJ *pxlo,
17: LONG xHot,
18: LONG yHot,
19: LONG x,
20: LONG y,
21: RECTL *prcl,
22: FLONG fl
23: );
24:
25: ULONG DrvSetMonoHwPointerShape(
26: SURFOBJ *pso,
27: SURFOBJ *psoMask,
28: SURFOBJ *psoColor,
29: XLATEOBJ *pxlo,
30: LONG xHot,
31: LONG yHot,
32: LONG x,
33: LONG y,
34: RECTL *prcl,
35: FLONG fl
36: );
37:
38: ULONG DrvSetBt485PointerShape(
39: SURFOBJ *pso,
40: SURFOBJ *psoMask,
41: SURFOBJ *psoColor,
42: XLATEOBJ *pxlo,
43: LONG xHot,
44: LONG yHot,
45: LONG x,
46: LONG y,
47: RECTL *prcl,
48: FLONG fl
49: );
50:
51:
52:
53: VOID DrvMoveColorPointer(SURFOBJ *pso,LONG x,LONG y,RECTL *prcl);
54: VOID DrvMoveHwPointer(SURFOBJ *pso,LONG x,LONG y,RECTL *prcl);
55: VOID DrvMoveBt485Pointer(SURFOBJ *pso, LONG x, LONG y, RECTL *prcl);
56:
57:
58:
59: /*****************************************************************************
60: * DrvMovePointer -
61: ****************************************************************************/
62: VOID DrvMovePointer(
63: SURFOBJ *pso,
64: LONG x,
65: LONG y,
66: RECTL *prcl)
67: {
68: PPDEV ppdev;
69:
70: ppdev = (PPDEV) pso->dhpdev;
71:
72: if (ppdev->flPointer & COLOR_POINTER)
73: DrvMoveColorPointer(pso, x, y, prcl);
74: else
75: {
76: if (ppdev->bBt485Dac == TRUE)
77: {
78: DrvMoveBt485Pointer(pso, x, y, prcl);
79: }
80: else
81: {
82: DrvMoveHwPointer(pso, x, y, prcl);
83: }
84: }
85: }
86:
87:
88: /*****************************************************************************
89: * DrvMoveColorPointer -
90: ****************************************************************************/
91: VOID DrvMoveColorPointer(
92: SURFOBJ *pso,
93: LONG x,
94: LONG y,
95: RECTL *prcl)
96: {
97: INT xDest, yDest;
98: WORD cmd;
99: RECTL rclClip;
100: PPDEV ppdev;
101:
102: ppdev = (PPDEV) pso->dhpdev;
103:
104: // Sync with the rest of the driver.
105:
106: GPWAIT();
107:
108: // If x is -1 then take down the cursor.
109:
110: if (x == -1)
111: {
112: ppdev->flPointer |= TAKE_DOWN_POINTER;
113: }
114:
115: // Adjust the actual position on the screen for the hot spot.
116:
117: xDest = x - ppdev->ptlHotSpot.x;
118: yDest = y - ppdev->ptlHotSpot.y;
119:
120: // If the save buffer has valid data, copy whats in the save
121: // buffer back to the screen.
122:
123: if (((ppdev->flPointer & VALID_SAVE_BUFFER) &&
124: (!(ppdev->flPointer & ANIMATEUPDATE))) ||
125: ((ppdev->flPointer & VALID_SAVE_BUFFER) &&
126: (ppdev->flPointer & ANIMATEUPDATE) &&
127: ((ppdev->ptlLastPosition.x != xDest) ||
128: (ppdev->ptlLastPosition.y != yDest))))
129: {
130: // The restore will always take place to the screen,
131: // so limit the clip area to the screen.
132:
133: rclClip.left = 0;
134: rclClip.top = 0;
135: rclClip.right = ppdev->cxScreen;
136: rclClip.bottom = ppdev->cyScreen;
137:
138: vSetS3ClipRect(ppdev, &rclClip);
139:
140: cmd = BITBLT | DRAW | DIR_TYPE_XY |
141: DRAWING_DIR_TBLRXM | WRITE;
142:
143: FIFOWAIT(FIFO_2_EMPTY);
144:
145: TEST_AND_SET_FRGD_MIX(SRC_DISPLAY_MEMORY | OVERPAINT);
146: OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));
147:
148: FIFOWAIT(FIFO_7_EMPTY);
149:
150: OUTPW(RECT_WIDTH, ppdev->szlPointer.cx - 1);
151: OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | ppdev->szlPointer.cy - 1));
152: OUTPW(CUR_X, COLOR_POINTER_SAVE_X);
153: OUTPW(CUR_Y, COLOR_POINTER_Y);
154: OUTPW(DEST_X, ppdev->ptlLastPosition.x);
155: OUTPW(DEST_Y, ppdev->ptlLastPosition.y);
156:
157: OUTPW(CMD, cmd);
158:
159: // Indicate that the save buffer is no longer valid.
160:
161: ppdev->flPointer &= ~VALID_SAVE_BUFFER;
162:
163: }
164:
165: // If we're just taking down the pointer then were done,
166: // so this an early exit.
167:
168: if (ppdev->flPointer & TAKE_DOWN_POINTER )
169: {
170: ppdev->flPointer &= ~TAKE_DOWN_POINTER;
171: vResetS3Clipping(ppdev);
172: return;
173: }
174:
175:
176: // Set the clipping for the pointers masks, save, and work area.
177:
178: rclClip.left = 0;
179: rclClip.top = COLOR_POINTER_Y;
180: rclClip.right = COLOR_POINTER_SAVE_X + COLOR_POINTER_CX;
181: rclClip.bottom = (COLOR_POINTER_Y + COLOR_POINTER_CY);
182:
183: vSetS3ClipRect(ppdev, &rclClip);
184:
185: if (!(ppdev->flPointer & ANIMATEUPDATE) ||
186: !(ppdev->flPointer & VALID_SAVE_BUFFER))
187: {
188: // Save the area under where we plan to draw the new cursor.
189:
190: cmd = BITBLT | DRAW | DIR_TYPE_XY |
191: DRAWING_DIR_TBLRXM | WRITE;
192:
193: FIFOWAIT(FIFO_2_EMPTY);
194:
195: TEST_AND_SET_FRGD_MIX(SRC_DISPLAY_MEMORY | OVERPAINT);
196: OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));
197:
198: FIFOWAIT(FIFO_7_EMPTY);
199:
200: OUTPW(RECT_WIDTH, ppdev->szlPointer.cx - 1);
201: OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | ppdev->szlPointer.cy - 1));
202: OUTPW(CUR_X, xDest);
203: OUTPW(CUR_Y, yDest);
204: OUTPW(DEST_X,COLOR_POINTER_SAVE_X);
205: OUTPW(DEST_Y,COLOR_POINTER_Y);
206:
207: OUTPW(CMD, cmd);
208: }
209:
210: // Validate the save buffer.
211:
212: ppdev->flPointer |= VALID_SAVE_BUFFER;
213:
214: // Now copy the saved data to the work buffer.
215:
216: cmd = BITBLT | DRAW | DIR_TYPE_XY |
217: DRAWING_DIR_TBLRXM | WRITE;
218:
219: FIFOWAIT(FIFO_2_EMPTY);
220:
221: TEST_AND_SET_FRGD_MIX(SRC_DISPLAY_MEMORY | OVERPAINT);
222: OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));
223:
224: FIFOWAIT(FIFO_7_EMPTY);
225:
226: OUTPW(RECT_WIDTH, ppdev->szlPointer.cx - 1);
227: OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | ppdev->szlPointer.cy - 1));
228: OUTPW(CUR_X, COLOR_POINTER_SAVE_X);
229: OUTPW(CUR_Y, COLOR_POINTER_Y);
230: OUTPW(DEST_X,COLOR_POINTER_WORK_X);
231: OUTPW(DEST_Y,COLOR_POINTER_Y);
232:
233: OUTPW(CMD, cmd);
234:
235: // Record the current position as the last position;
236:
237: ppdev->ptlLastPosition.x = xDest;
238: ppdev->ptlLastPosition.y = yDest;
239:
240: // AND in the AND mask to the work buffer
241:
242: cmd = BITBLT | DRAW | DIR_TYPE_XY |
243: DRAWING_DIR_TBLRXM | MULTIPLE_PIXELS | WRITE;
244:
245: FIFOWAIT(FIFO_6_EMPTY);
246:
247: TEST_AND_SET_FRGD_MIX(FOREGROUND_COLOR | SCREEN_AND_NEW);
248: TEST_AND_SET_FRGD_COLOR(0xff);
249: TEST_AND_SET_BKGD_MIX(BACKGROUND_COLOR | SCREEN_AND_NEW);
250: SET_BKGD_COLOR(0x00);
251: OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | DISPLAY_MEMORY));
252: TEST_AND_SET_RD_MASK(COLOR_POINTER_AND_PLANE);
253:
254:
255: FIFOWAIT(FIFO_7_EMPTY);
256:
257: OUTPW(RECT_WIDTH, ppdev->szlPointer.cx - 1);
258: OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | ppdev->szlPointer.cy - 1));
259: OUTPW(CUR_X, 0);
260: OUTPW(CUR_Y, COLOR_POINTER_Y);
261: OUTPW(DEST_X,COLOR_POINTER_WORK_X);
262: OUTPW(DEST_Y,COLOR_POINTER_Y);
263: OUTPW(CMD, cmd);
264:
265: FIFOWAIT(FIFO_1_EMPTY);
266:
267: TEST_AND_SET_RD_MASK(0xff);
268:
269: // Or in the Color data to the work buffer.
270:
271: cmd = BITBLT | DRAW | DIR_TYPE_XY |
272: DRAWING_DIR_TBLRXM | SINGLE_PIXEL | WRITE;
273:
274: FIFOWAIT(FIFO_3_EMPTY);
275:
276: TEST_AND_SET_FRGD_MIX(SRC_DISPLAY_MEMORY | SCREEN_OR_NEW);
277: OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));
278:
279: FIFOWAIT(FIFO_7_EMPTY);
280:
281: OUTPW(RECT_WIDTH, ppdev->szlPointer.cx - 1);
282: OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | ppdev->szlPointer.cy - 1));
283: OUTPW(CUR_X, COLOR_POINTER_COLOR_DATA_X);
284: OUTPW(CUR_Y, COLOR_POINTER_Y);
285: OUTPW(DEST_X,COLOR_POINTER_WORK_X);
286: OUTPW(DEST_Y,COLOR_POINTER_Y);
287: OUTPW(CMD, cmd);
288:
289: // Now copy the work buffer to the screen.
290:
291: rclClip.left = 0;
292: rclClip.top = 0;
293: rclClip.right = ppdev->cxScreen;
294: rclClip.bottom = ppdev->cyScreen;
295:
296: vSetS3ClipRect(ppdev, &rclClip);
297:
298: cmd = BITBLT | DRAW | DIR_TYPE_XY |
299: DRAWING_DIR_TBLRXM | WRITE;
300:
301: FIFOWAIT(FIFO_2_EMPTY);
302:
303: TEST_AND_SET_FRGD_MIX(SRC_DISPLAY_MEMORY | OVERPAINT);
304: OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));
305:
306: FIFOWAIT(FIFO_7_EMPTY);
307:
308: OUTPW(RECT_WIDTH, ppdev->szlPointer.cx - 1);
309: OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | ppdev->szlPointer.cy - 1));
310: OUTPW(CUR_X, COLOR_POINTER_WORK_X);
311: OUTPW(CUR_Y, COLOR_POINTER_Y);
312: OUTPW(DEST_X, xDest);
313: OUTPW(DEST_Y, yDest);
314: OUTPW(CMD, cmd);
315:
316: FIFOWAIT(FIFO_1_EMPTY);
317:
318: TEST_AND_SET_RD_MASK(0xff);
319:
320: // If the GDI requests the bounding box of the pointer,
321: // return it.
322:
323: if (prcl != NULL)
324: {
325: prcl->left = xDest;
326: prcl->top = yDest;
327: prcl->right = xDest + ppdev->szlPointer.cx;
328: prcl->bottom = yDest + ppdev->szlPointer.cy;
329:
330: }
331:
332: // Reset the clipping. This can go when no H/W clipping is done
333: // on the color pointer.
334:
335: vResetS3Clipping(ppdev);
336: }
337:
338: /*****************************************************************************
339: * DrvMoveHwPointer -
340: ****************************************************************************/
341: VOID DrvMoveHwPointer(
342: SURFOBJ *pso,
343: LONG x,
344: LONG y,
345: RECTL *prcl)
346: {
347: WORD msb, lsb;
348:
349: PPDEV ppdev;
350:
351: ppdev = (PPDEV) pso->dhpdev;
352:
353: // Save the CRTC Index.
354:
355: ppdev->CrtcIndex = INP(CRTC_INDEX);
356:
357: // If x is -1 then take down the cursor.
358:
359: if (x == -1)
360: {
361: OUTPW (CRTC_INDEX, (ppdev->HgcMode & ~(HGC_ENABLE << 8)));
362: }
363:
364: // Adjust the actual pointer position depending upon
365: // the hot spot.
366:
367: x -= ppdev->ptlHotSpot.x;
368: y -= ppdev->ptlHotSpot.y;
369:
370: // Record the current position as the last position;
371:
372: ppdev->ptlLastPosition.x = x;
373: ppdev->ptlLastPosition.y = y;
374:
375: if (x <= 0)
376: {
377: OUTPW (CRTC_INDEX, ((-x << 8) | HGC_DX));
378: x = 0;
379: }
380: else
381: {
382: OUTPW (CRTC_INDEX, ((0 << 8) | HGC_DX));
383: }
384:
385: if (y <= 0)
386: {
387: OUTPW (CRTC_INDEX, ((-y << 8) | HGC_DY));
388: y = 0;
389: }
390: else
391: {
392: OUTPW (CRTC_INDEX, ((0 << 8) | HGC_DY));
393: }
394:
395: // Set the position of the cursor.
396:
397: msb = HIBYTE (x);
398: lsb = LOBYTE (x);
399: OUTPW (CRTC_INDEX, ((lsb << 8) | HGC_ORGX_LSB));
400: OUTPW (CRTC_INDEX, ((msb << 8) | HGC_ORGX_MSB));
401:
402: msb = HIBYTE (y);
403: lsb = LOBYTE (y);
404: OUTPW (CRTC_INDEX, ((lsb << 8) | HGC_ORGY_LSB));
405: OUTPW (CRTC_INDEX, ((msb << 8) | HGC_ORGY_MSB));
406:
407: // Restore the CRTC index
408:
409: OUTP(CRTC_INDEX, ppdev->CrtcIndex);
410:
411: }
412:
413: /*****************************************************************************
414: * DrvMoveBt485Pointer -
415: ****************************************************************************/
416: VOID DrvMoveBt485Pointer(
417: SURFOBJ *pso,
418: LONG x,
419: LONG y,
420: RECTL *prcl)
421: {
422: WORD msb, lsb;
423:
424: PPDEV ppdev;
425:
426: ppdev = (PPDEV) pso->dhpdev;
427:
428: // Save the CRTC Index.
429:
430: ppdev->CrtcIndex = INP(CRTC_INDEX);
431:
432: // If x is -1 then take down the cursor.
433:
434: if (x == -1)
435: {
436: OUTPW (CRTC_INDEX, (ppdev->ExtDacCtl | 0x0200));
437: OUTP (BT485_ADDR_CMD_REG2, ppdev->Bt485CmdReg2 & BT485_CURSOR_DISABLE);
438: OUTPW (CRTC_INDEX, (ppdev->ExtDacCtl));
439:
440: return;
441: }
442:
443: // Adjust the actual pointer position depending upon
444: // the hot spot.
445:
446: x -= ppdev->ptlHotSpot.x;
447: y -= ppdev->ptlHotSpot.y;
448:
449: // Record the current position as the last position;
450:
451: ppdev->ptlLastPosition.x = x;
452: ppdev->ptlLastPosition.y = y;
453:
454: // Adjust for the placement in the Bt485
455:
456: x += 64;
457: y += 64;
458:
459: // Set the position of the cursor.
460:
461: OUTPW (CRTC_INDEX, (ppdev->ExtDacCtl | 0x0300));
462:
463: msb = HIBYTE (x);
464: lsb = LOBYTE (x);
465:
466: OUTP(BT485_CURSOR_X_LOW, lsb);
467: OUTP(BT485_CURSOR_X_HIGH, msb);
468:
469: msb = HIBYTE (y);
470: lsb = LOBYTE (y);
471:
472: OUTP(BT485_CURSOR_Y_LOW, lsb);
473: OUTP(BT485_CURSOR_Y_HIGH, msb);
474:
475: OUTPW (CRTC_INDEX, (ppdev->ExtDacCtl));
476:
477: // Restore the CRTC index
478:
479: OUTP(CRTC_INDEX, ppdev->CrtcIndex);
480:
481: }
482:
483:
484:
485: /*****************************************************************************
486: * DrvSetPointerShape -
487: ****************************************************************************/
488: ULONG DrvSetPointerShape(
489: SURFOBJ *pso,
490: SURFOBJ *psoMask,
491: SURFOBJ *psoColor,
492: XLATEOBJ *pxlo,
493: LONG xHot,
494: LONG yHot,
495: LONG x,
496: LONG y,
497: RECTL *prcl,
498: FLONG fl)
499: {
500: ULONG ulRet;
501: PPDEV ppdev;
502: BOOL bResetAnimateFlag;
503:
504: ppdev = (PPDEV) pso->dhpdev;
505:
506: // Save the hot spot in the pdev.
507:
508: ppdev->ptlHotSpot.x = xHot;
509: ppdev->ptlHotSpot.y = yHot;
510:
511: ppdev->szlPointer.cx = psoMask->sizlBitmap.cx;
512: ppdev->szlPointer.cy = psoMask->sizlBitmap.cy / 2;
513:
514: // The pointer may be larger than we can handle.
515: // If it is we must cleanup the screen and let the engine
516: // take care of it.
517:
518: if (psoMask->sizlBitmap.cx > 64 || psoMask->sizlBitmap.cy > 64)
519: {
520: // If it's a color pointer take it down.
521:
522: if ( (ppdev->flPointer & COLOR_POINTER)
523: && (ppdev->flPointer & VALID_SAVE_BUFFER)
524: )
525: {
526: ulRet = DrvSetColorPointerShape(pso, NULL, NULL, NULL,
527: 0, 0, 0, 0, NULL, 0);
528: }
529:
530: // Disable the mono hardware pointer.
531:
532: if (ppdev->bBt485Dac == TRUE)
533: {
534: // Disable the H/W cursor on the Bt 485.
535:
536: OUTPW (CRTC_INDEX, (ppdev->ExtDacCtl | 0x0200));
537: OUTP (BT485_ADDR_CMD_REG2, ppdev->Bt485CmdReg2 & BT485_CURSOR_DISABLE);
538: OUTPW (CRTC_INDEX, (ppdev->ExtDacCtl));
539:
540: }
541: else
542: {
543: OUTPW (CRTC_INDEX, (ppdev->HgcMode & ~(HGC_ENABLE << 8)));
544: }
545:
546: // reset our local pointer flags.
547:
548: ppdev->flPointer = 0;
549:
550: return (SPS_DECLINE);
551:
552: }
553:
554: // Set the AnimateUpdate flag.
555:
556: if ((fl & SPS_ANIMATEUPDATE) &&
557: ((x - xHot) == ppdev->ptlLastPosition.x) &&
558: ((y - yHot) == ppdev->ptlLastPosition.y))
559: {
560: ppdev->flPointer |= ANIMATEUPDATE;
561: }
562: else
563: {
564: ppdev->flPointer &= ~ANIMATEUPDATE;
565: }
566:
567: if (psoColor != NULL)
568: {
569: // Disable the mono hardware pointer.
570:
571: if (ppdev->bBt485Dac == TRUE)
572: {
573: // Disable the H/W cursor on the Bt 485.
574:
575: OUTPW (CRTC_INDEX, (ppdev->ExtDacCtl | 0x0200));
576: OUTP (BT485_ADDR_CMD_REG2, ppdev->Bt485CmdReg2 & BT485_CURSOR_DISABLE);
577: OUTPW (CRTC_INDEX, (ppdev->ExtDacCtl));
578:
579: }
580: else
581: {
582: OUTPW (CRTC_INDEX, (ppdev->HgcMode & ~(HGC_ENABLE << 8)));
583: }
584:
585: ppdev->flPointer |= COLOR_POINTER;
586: ulRet = DrvSetColorPointerShape(pso, psoMask, psoColor, pxlo,
587: xHot, yHot, x, y, prcl, fl);
588:
589: }
590: else
591: {
592: // Take down the color pointer if it is visible.
593:
594: if ( (ppdev->flPointer & COLOR_POINTER)
595: && (ppdev->flPointer & VALID_SAVE_BUFFER)
596: )
597: {
598: // If we are making a transition from a color to a monochrome pointer
599: // and we haven't moved the pointer, and we're still in Animate Update mode,
600: // then we have to turn off the Animate Update flag for this call to turn off
601: // the color pointer.
602:
603: if (ppdev->flPointer & ANIMATEUPDATE)
604: {
605: bResetAnimateFlag = TRUE;
606: ppdev->flPointer &= ~ANIMATEUPDATE;
607: }
608: else
609: {
610: bResetAnimateFlag = FALSE;
611: }
612:
613: ulRet = DrvSetColorPointerShape(pso, NULL, NULL, NULL,
614: 0, 0, 0, 0, NULL, 0);
615:
616: if (bResetAnimateFlag == TRUE)
617: {
618: ppdev->flPointer |= ANIMATEUPDATE;
619: }
620: }
621:
622: // Take care of the monochrome pointer.
623:
624: ppdev->flPointer &= ~COLOR_POINTER;
625:
626: if (ppdev->bBt485Dac == TRUE)
627: {
628: ulRet = DrvSetBt485PointerShape(pso, psoMask, psoColor, pxlo,
629: xHot, yHot, x, y, prcl, fl);
630: }
631: else
632: {
633: ulRet = DrvSetMonoHwPointerShape(pso, psoMask, psoColor, pxlo,
634: xHot, yHot, x, y, prcl, fl);
635: }
636: }
637:
638: // Now that we have done an animation update, revert to a normal
639: // pointer.
640:
641: if (ppdev->flPointer & ANIMATEUPDATE)
642: {
643: ppdev->flPointer &= ~ANIMATEUPDATE;
644: }
645:
646: return (ulRet);
647: }
648:
649:
650: /*****************************************************************************
651: * DrvSetColorPointerShape -
652: ****************************************************************************/
653: ULONG DrvSetColorPointerShape(
654: SURFOBJ *pso,
655: SURFOBJ *psoMask,
656: SURFOBJ *psoColor,
657: XLATEOBJ *pxlo,
658: LONG xHot,
659: LONG yHot,
660: LONG x,
661: LONG y,
662: RECTL *prcl,
663: FLONG fl)
664: {
665: UINT i, j, k;
666: PBYTE pb;
667: WORD Cmd;
668: UINT cxMask, cyMask, cyAND, cyXOR, cxANDBytes, cxXORBytes;
669: UINT cxColor, cyColor;
670: PBYTE pjAND, pjXOR, pjColor;
671: LONG lDelta, lColorDelta;
672: PBYTE pb4Bpp;
673: UINT nSrcBytes;
674: PULONG pulXlate;
675: RECTL rclClip;
676: PPDEV ppdev;
677: BYTE LineBuff8Bpp[64];
678:
679: DISPDBG((3, "S3.DLL:DrvSetColorPointerShape - Entry\n"));
680:
681: ppdev = (PPDEV) pso->dhpdev;
682:
683: // Sync this operation with the rest of the driver.
684:
685: GPWAIT();
686:
687: // Remove the current pointer, if it is on the screen.
688:
689: if ((ppdev->flPointer & VALID_SAVE_BUFFER) && (!(ppdev->flPointer & ANIMATEUPDATE)))
690: {
691: DrvMovePointer(pso, -1, -1, NULL);
692: }
693:
694: // If the pointer is completely transparent, then just return.
695:
696: if (psoMask == NULL)
697: {
698: return (SPS_ACCEPT_EXCLUDE);
699: }
700:
701: // If the GDI requests the bounding box of the pointer,
702: // return it.
703:
704: if (prcl != NULL)
705: {
706: cxMask = psoMask->sizlBitmap.cx;
707: cyMask = psoMask->sizlBitmap.cy;
708:
709: prcl->left = x - xHot;
710: prcl->top = y - yHot;
711: prcl->right = prcl->left + cxMask;
712: prcl->bottom = prcl->top + cyMask;
713:
714: }
715:
716: // Set the clipping.
717:
718: rclClip.left = 0;
719: rclClip.top = COLOR_POINTER_Y;
720: rclClip.right = COLOR_POINTER_CX * 3;
721: rclClip.bottom = COLOR_POINTER_Y + COLOR_POINTER_CY;
722:
723: vSetS3ClipRect(ppdev, &rclClip);
724:
725: // Get the bitmap dimensions.
726:
727: cxMask = psoMask->sizlBitmap.cx;
728: cyMask = psoMask->sizlBitmap.cy;
729:
730: cxColor = psoColor->sizlBitmap.cx;
731: cyColor = psoColor->sizlBitmap.cy;
732:
733: cyAND = cyXOR = cyMask / 2;
734: cxANDBytes = cxXORBytes = cxMask / 8;
735:
736: // Set up pointers to the AND and XOR masks.
737:
738: pjAND = psoMask->pvScan0;
739: lDelta = psoMask->lDelta;
740: pjXOR = pjAND + (cyAND * lDelta);
741:
742: pjColor = psoColor->pvScan0;
743: lColorDelta = psoColor->lDelta;
744:
745: // Zero out coprocessor memory for the masks, color data,
746: // and the work area. Be carefull not to wipe out the save area.
747:
748: Cmd = RECTANGLE_FILL |
749: DRAW | DRAWING_DIR_TBLRXM | DIR_TYPE_XY |
750: LAST_PIXEL_ON | SINGLE_PIXEL | WRITE;
751:
752: FIFOWAIT(FIFO_3_EMPTY);
753:
754: TEST_AND_SET_FRGD_MIX(LOGICAL_0);
755: TEST_AND_SET_WRT_MASK(0xff);
756: OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));
757:
758: FIFOWAIT(FIFO_5_EMPTY);
759:
760: OUTPW(CUR_X, 0);
761: OUTPW(CUR_Y, COLOR_POINTER_Y);
762: OUTPW(RECT_WIDTH, (COLOR_POINTER_CX * 2) - 1);
763: OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | COLOR_POINTER_CY - 1));
764: OUTPW(CMD, Cmd);
765:
766: // Copy the AND mask to the off screen memory.
767:
768: Cmd = RECTANGLE_FILL | BUS_SIZE_8 | WAIT |
769: DRAW | DRAWING_DIR_TBLRXM | DIR_TYPE_XY |
770: LAST_PIXEL_ON | MULTIPLE_PIXELS | WRITE;
771:
772:
773: FIFOWAIT(FIFO_6_EMPTY);
774:
775: TEST_AND_SET_FRGD_MIX(FOREGROUND_COLOR | OVERPAINT);
776: TEST_AND_SET_FRGD_COLOR(0xff);
777: TEST_AND_SET_BKGD_MIX(BACKGROUND_COLOR | OVERPAINT);
778: SET_BKGD_COLOR(0x00);
779: TEST_AND_SET_WRT_MASK(COLOR_POINTER_AND_PLANE);
780: OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | CPU_DATA));
781:
782: FIFOWAIT(FIFO_5_EMPTY);
783:
784: OUTPW(CUR_X, 0);
785: OUTPW(CUR_Y, COLOR_POINTER_Y);
786: OUTPW(RECT_WIDTH, cxMask - 1);
787: OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | cyAND - 1));
788:
789: GPWAIT();
790:
791: OUTPW(CMD, Cmd);
792:
793: CHECK_DATA_READY;
794:
795: // Now transfer the AND mask data.
796:
797: FIFOWAIT(FIFO_8_EMPTY);
798:
799: pb = pjAND;
800: for (i = 0; i < cyAND; i++)
801: {
802: vDataPortOutB(ppdev,(PBYTE) pb, cxANDBytes);
803: pb += lDelta;
804: }
805:
806: CHECK_DATA_COMPLETE;
807:
808:
809: // Copy the XOR mask to the off screen memory.
810:
811: Cmd = RECTANGLE_FILL | BUS_SIZE_8 | WAIT |
812: DRAW | DRAWING_DIR_TBLRXM | DIR_TYPE_XY |
813: LAST_PIXEL_ON | MULTIPLE_PIXELS | WRITE;
814:
815: FIFOWAIT(FIFO_6_EMPTY);
816:
817: TEST_AND_SET_FRGD_MIX(FOREGROUND_COLOR | OVERPAINT);
818: TEST_AND_SET_FRGD_COLOR(0xff);
819: TEST_AND_SET_BKGD_MIX(BACKGROUND_COLOR | OVERPAINT);
820: SET_BKGD_COLOR(0x00);
821: TEST_AND_SET_WRT_MASK(COLOR_POINTER_XOR_PLANE);
822: OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | CPU_DATA));
823:
824: FIFOWAIT(FIFO_5_EMPTY);
825:
826: OUTPW(CUR_X, 0);
827: OUTPW(CUR_Y, COLOR_POINTER_Y);
828: OUTPW(RECT_WIDTH, cxMask - 1);
829: OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | cyXOR - 1));
830:
831: GPWAIT();
832:
833: OUTPW(CMD, Cmd);
834:
835: // Now transfer the XOR mask data.
836:
837: FIFOWAIT(FIFO_8_EMPTY);
838:
839: pb = pjXOR;
840: for (i = 0; i < cyXOR; i++)
841: {
842: vDataPortOutB(ppdev,(PBYTE) pb, cxXORBytes);
843: pb += lDelta;
844: }
845:
846: FIFOWAIT(FIFO_1_EMPTY);
847:
848: TEST_AND_SET_WRT_MASK(0xff);
849:
850: if (psoColor->iBitmapFormat == BMF_8BPP)
851: {
852: // Copy the Color mask to the off screen memory.
853:
854: Cmd = RECTANGLE_FILL | BUS_SIZE_8 | WAIT |
855: DRAW | DRAWING_DIR_TBLRXM | DIR_TYPE_XY |
856: LAST_PIXEL_ON | SINGLE_PIXEL | WRITE;
857:
858: FIFOWAIT(FIFO_7_EMPTY);
859:
860: TEST_AND_SET_FRGD_MIX(SRC_CPU_DATA | OVERPAINT);
861: OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));
862: OUTPW(CUR_X, COLOR_POINTER_CX);
863: OUTPW(CUR_Y, COLOR_POINTER_Y);
864: OUTPW(RECT_WIDTH, cxColor - 1);
865: OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | cyColor - 1));
866:
867: GPWAIT();
868:
869: OUTPW(CMD, Cmd);
870:
871: CHECK_DATA_READY;
872:
873: // Now transfer the data.
874:
875: // Note: It would nice to do the entire bitmap in one
876: // fell swoop, but there is no gaurantee source will
877: // wrap on a bitmap boundary.
878:
879: FIFOWAIT(FIFO_8_EMPTY);
880:
881: pb = pjColor;
882: for (i = 0; i < cyColor; i++)
883: {
884: vDataPortOutB(ppdev,pb, cxColor);
885: pb += lColorDelta;
886: }
887:
888: CHECK_DATA_COMPLETE;
889: }
890: else
891: {
892: Cmd = RECTANGLE_FILL | BUS_SIZE_8 | WAIT |
893: DRAW | DRAWING_DIR_TBLRXM | DIR_TYPE_XY |
894: LAST_PIXEL_ON | SINGLE_PIXEL | WRITE;
895:
896: FIFOWAIT(FIFO_7_EMPTY);
897:
898: TEST_AND_SET_FRGD_MIX(SRC_CPU_DATA | OVERPAINT);
899: OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));
900: OUTPW(CUR_X, COLOR_POINTER_CX);
901: OUTPW(CUR_Y, COLOR_POINTER_Y);
902: OUTPW(RECT_WIDTH, cxColor - 1);
903: OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | cyColor - 1));
904:
905: GPWAIT();
906:
907: OUTPW(CMD, Cmd);
908:
909: CHECK_DATA_READY;
910:
911: // Now transfer the data.
912:
913: // Note: It would nice to do the entire bitmap in one
914: // fell swoop, but there is no gaurantee source will
915: // wrap on a bitmap boundary.
916:
917: pb4Bpp = pjColor;
918: nSrcBytes = (cxColor + 1) / 2;
919:
920: if (pxlo->flXlate & XO_TABLE)
921: {
922: pulXlate = pxlo->pulXlate;
923: }
924: else
925: {
926: pulXlate = XLATEOBJ_piVector(pxlo);
927: }
928:
929: FIFOWAIT(FIFO_8_EMPTY);
930:
931: for (i = 0; i < cyColor; i++)
932: {
933: for (k = 0, j = 0; j < nSrcBytes; j++)
934: {
935: LineBuff8Bpp[k++] = (BYTE) pulXlate[(pb4Bpp[j] & 0xF0) >> 4];
936: LineBuff8Bpp[k++] = (BYTE) pulXlate[pb4Bpp[j] & 0x0F];
937: }
938:
939: vDataPortOutB(ppdev,LineBuff8Bpp, cxColor);
940: pb4Bpp += lColorDelta;
941: }
942:
943: CHECK_DATA_COMPLETE;
944: }
945:
946: // Set the position of the cursor.
947:
948: DrvMovePointer(pso, x, y, NULL);
949:
950: return (SPS_ACCEPT_EXCLUDE);
951: }
952:
953: /*****************************************************************************
954: * DrvSetMonoHwPointerShape -
955: ****************************************************************************/
956: ULONG DrvSetMonoHwPointerShape(
957: SURFOBJ *pso,
958: SURFOBJ *psoMask,
959: SURFOBJ *psoColor,
960: XLATEOBJ *pxlo,
961: LONG xHot,
962: LONG yHot,
963: LONG x,
964: LONG y,
965: RECTL *prcl,
966: FLONG fl)
967: {
968: UINT i, j, k, cxMask, cyMask, cyAND, cxAND, cyXOR, cxXOR;
969: PBYTE pjAND, pjXOR;
970: PWORD pwS3AndMask, pwS3XorMask, pInterleavedMasks;
971: WORD msb, lsb;
972: INT lDelta;
973: PPDEV ppdev;
974: WORD PointerDataY;
975: BYTE s3AndMask[64][8],
976: s3XorMask[64][8];
977: WORD s3InterleavedMasks[512];
978:
979: DISPDBG((3, "S3.DLL:DrvSetMonoHwPointerShape - Entry\n"));
980:
981: ppdev = (PPDEV) pso->dhpdev;
982:
983: // If the mask is NULL this implies the pointer is not
984: // visible.
985:
986: if (psoMask == NULL)
987: {
988: OUTPW (CRTC_INDEX, (ppdev->HgcMode & ~(HGC_ENABLE << 8)));
989: return (SPS_ACCEPT_NOEXCLUDE);
990: }
991:
992: // Init the AND and XOR masks.
993:
994: memset (s3AndMask, 0xFF, 512);
995: memset (s3XorMask, 0x00, 512);
996:
997: // Get the bitmap dimensions.
998:
999: cxMask = psoMask->sizlBitmap.cx;
1000: cyMask = psoMask->sizlBitmap.cy;
1001:
1002: cyAND = cyXOR = cyMask / 2;
1003: cxAND = cxXOR = cxMask / 8;
1004:
1005: // Set up pointers to the AND and XOR masks.
1006:
1007: pjAND = psoMask->pvScan0;
1008: lDelta = psoMask->lDelta;
1009: pjXOR = pjAND + (cyAND * lDelta);
1010:
1011: // Copy the AND mask.
1012:
1013: memset (s3AndMask, 0xFFFFFFFF, 512);
1014: memset (s3XorMask, 0, 512);
1015:
1016: for (i = 0; i < cyAND; i++)
1017: {
1018: // Copy over a line of the AND mask.
1019:
1020: for (j = 0; j < cxAND; j++)
1021: {
1022: s3AndMask[i][j] = pjAND[j];
1023: }
1024:
1025: // point to the next line of the AND mask.
1026:
1027: pjAND += lDelta;
1028: }
1029:
1030: // Copy the XOR mask.
1031:
1032: for (i = 0; i < cyXOR; i++)
1033: {
1034: // Copy over a line of the XOR mask.
1035:
1036: for (j = 0; j < cxXOR; j++)
1037: {
1038: s3XorMask[i][j] = pjXOR[j];
1039: }
1040:
1041: // point to the next line of the XOR mask.
1042:
1043: pjXOR += lDelta;
1044: }
1045:
1046: // Interleave the S3AND and S3XOR masks.
1047: // This will allow an rep stosw instruction to down
1048: // load the mask to the card..
1049:
1050: pwS3AndMask = (PWORD) s3AndMask;
1051: pwS3XorMask = (PWORD) s3XorMask;
1052: pInterleavedMasks = s3InterleavedMasks;
1053:
1054: // Interleave the S3AND and S3XOR masks.
1055: // This will allow an rep stosw instruction to down
1056: // load the mask to the card..
1057:
1058: pwS3AndMask = (PWORD) s3AndMask;
1059: pwS3XorMask = (PWORD) s3XorMask;
1060: pInterleavedMasks = s3InterleavedMasks;
1061:
1062: k = 256;
1063:
1064: for (i = 0; i < k; i++)
1065: {
1066: *pInterleavedMasks++ = *pwS3AndMask++;
1067: *pInterleavedMasks++ = *pwS3XorMask++;
1068: }
1069:
1070: // Down load the pointer shape to the S3 chip.
1071: // Determine Which Monochrome pointer buffer is available.
1072:
1073: if (ppdev->MonoPointerData == 0)
1074: {
1075: PointerDataY = LOWORD (PTR_DATA_Y);
1076: ppdev->MonoPointerData = 1;
1077: }
1078: else
1079: {
1080: PointerDataY = LOWORD (PTR_DATA_Y - 1);
1081: ppdev->MonoPointerData = 0;
1082: }
1083:
1084: // Set the clipping window to include the last line of offscreen
1085: // memory.
1086:
1087: vResetS3Clipping(ppdev);
1088:
1089: // Wait for Fifo 7 to empty.
1090:
1091: FIFOWAIT(FIFO_7_EMPTY);
1092:
1093: // Now set up for the rectangle.
1094:
1095: OUTPW (CUR_X, PTR_DATA_X);
1096: OUTPW (CUR_Y, PointerDataY);
1097:
1098: OUTPW (RECT_WIDTH, ppdev->cxMaxRam - 1);
1099: OUTPW (MULTIFUNC_CNTL, RECT_HEIGHT);
1100:
1101: OUTPW (MULTIFUNC_CNTL, DATA_EXTENSION);
1102:
1103: TEST_AND_SET_FRGD_MIX(SRC_CPU_DATA | OVERPAINT);
1104:
1105: GPWAIT();
1106:
1107: OUTPW (CMD, (RECTANGLE_FILL | BYTE_SWAP |
1108: BUS_SIZE_16 | WAIT |
1109: DRAWING_DIR_TBLRXM |
1110: DRAW | WRITE));
1111:
1112: CHECK_DATA_READY;
1113:
1114: // Now down load the masks.
1115:
1116: vDataPortOut(ppdev, (PWORD) s3InterleavedMasks, 512);
1117:
1118: CHECK_DATA_COMPLETE;
1119:
1120: // This should do it. (the cursor should be downloaded)
1121: // Now, wasn't that simple, NOT!
1122:
1123: // Note: At one point we disabled the pointer before setting the new one.
1124: // this caused an excessive flicker of the pointer, so we removed the disable.
1125: // At another point, we found the cursor jumped to the left for 1 frame if
1126: // we did not disable the cursor for non-animated pointers. So, now
1127: // we handle each case separately.
1128:
1129:
1130: if (!(ppdev->flPointer & ANIMATEUPDATE))
1131: {
1132: OUTPW (CRTC_INDEX, (ppdev->HgcMode | (HGC_DISABLE << 8)));
1133: }
1134:
1135: // Set the hardware graphics cursor storage area start address for
1136: // the new pointer data.
1137:
1138: msb = HIBYTE (PointerDataY);
1139: lsb = LOBYTE (PointerDataY);
1140:
1141: OUTPW (CRTC_INDEX, ((msb << 8) | CR4C));
1142: OUTPW (CRTC_INDEX, ((lsb << 8) | CR4D));
1143:
1144: // Set the position of the cursor.
1145: // Need to do this twice, due to shadowing in the 928 and 801/805.
1146:
1147: DrvMoveHwPointer(pso, x, y, NULL);
1148:
1149: // On the 928, 801/805 wait for vertical interval then set the
1150: // position a second time.
1151:
1152: if (ppdev->s3ChipID >= 0x90)
1153: {
1154: while (INP(STATUS_1) & VSY_NOT);
1155: DrvMoveHwPointer(pso, x, y, NULL);
1156:
1157: while (!(INP(STATUS_1) & VSY_NOT));
1158: DrvMoveHwPointer(pso, x, y, NULL);
1159:
1160: }
1161:
1162: OUTPW (CRTC_INDEX, (ppdev->HgcMode | (HGC_ENABLE << 8)));
1163:
1164: // Reset the clipping. This can go when no H/W clipping is done
1165: // on the color pointer.
1166:
1167: vResetS3Clipping(ppdev);
1168:
1169: return (SPS_ACCEPT_NOEXCLUDE);
1170: }
1171:
1172:
1173:
1174: /*****************************************************************************
1175: * DrvSetBt485PointerShape -
1176: ****************************************************************************/
1177: ULONG DrvSetBt485PointerShape(
1178: SURFOBJ *pso,
1179: SURFOBJ *psoMask,
1180: SURFOBJ *psoColor,
1181: XLATEOBJ *pxlo,
1182: LONG xHot,
1183: LONG yHot,
1184: LONG x,
1185: LONG y,
1186: RECTL *prcl,
1187: FLONG fl)
1188: {
1189: UINT i, j, cxMask, cyMask, cyAND, cxAND, cyXOR, cxXOR;
1190: PBYTE pjAND, pjXOR;
1191: INT lDelta;
1192: PPDEV ppdev;
1193: BYTE s3AndMask[64][8],
1194: s3XorMask[64][8];
1195:
1196: DISPDBG((3, "S3.DLL:DrvSetBt485PointerShape - Entry\n"));
1197:
1198: ppdev = (PPDEV) pso->dhpdev;
1199:
1200: // If the mask is NULL this implies the pointer is not
1201: // visible.
1202:
1203: if (psoMask == NULL)
1204: {
1205: // Disable the H/W cursor on the Bt 485.
1206:
1207: OUTPW (CRTC_INDEX, (ppdev->ExtDacCtl | 0x0200));
1208: OUTP (BT485_ADDR_CMD_REG2, ppdev->Bt485CmdReg2 & BT485_CURSOR_DISABLE);
1209: OUTPW (CRTC_INDEX, (ppdev->ExtDacCtl));
1210:
1211: return (SPS_ACCEPT_NOEXCLUDE);
1212: }
1213:
1214: // Init the AND and XOR masks.
1215:
1216: memset (s3AndMask, 0xFF, 512);
1217: memset (s3XorMask, 0x00, 512);
1218:
1219: // Get the bitmap dimensions.
1220:
1221: cxMask = psoMask->sizlBitmap.cx;
1222: cyMask = psoMask->sizlBitmap.cy;
1223:
1224: cyAND = cyXOR = cyMask / 2;
1225: cxAND = cxXOR = cxMask / 8;
1226:
1227: // Set up pointers to the AND and XOR masks.
1228:
1229: pjAND = psoMask->pvScan0;
1230: lDelta = psoMask->lDelta;
1231: pjXOR = pjAND + (cyAND * lDelta);
1232:
1233: // Copy the AND mask.
1234:
1235: for (i = 0; i < cyAND; i++)
1236: {
1237: // Copy over a line of the AND mask.
1238:
1239: for (j = 0; j < cxAND; j++)
1240: {
1241: s3AndMask[i][j] = pjAND[j];
1242: }
1243:
1244: // point to the next line of the AND mask.
1245:
1246: pjAND += lDelta;
1247: }
1248:
1249: // Copy the XOR mask.
1250:
1251: for (i = 0; i < cyXOR; i++)
1252: {
1253: // Copy over a line of the XOR mask.
1254:
1255: for (j = 0; j < cxXOR; j++)
1256: {
1257: s3XorMask[i][j] = pjXOR[j];
1258: }
1259:
1260: // point to the next line of the XOR mask.
1261:
1262: pjXOR += lDelta;
1263: }
1264:
1265: pjAND = (PBYTE) s3AndMask;
1266: pjXOR = (PBYTE) s3XorMask;
1267:
1268: // Set the cursor for 64 X 64, and set the 2 MSB's for the cursor
1269: // RAM addr to 0.
1270: // First get access to Command Register 3
1271:
1272: OUTPW (CRTC_INDEX, (ppdev->ExtDacCtl | 0x0100));
1273: OUTP (BT485_ADDR_CMD_REG0, ppdev->Bt485CmdReg0 | BT485_CMD_REG_3_ACCESS);
1274:
1275: OUTPW (CRTC_INDEX, ppdev->ExtDacCtl);
1276: OUTP (0x3c8, 0x01);
1277:
1278: OUTPW (CRTC_INDEX, (ppdev->ExtDacCtl | 0x0200));
1279: OUTP (BT485_ADDR_CMD_REG3, ppdev->Bt485CmdReg3);
1280:
1281: // Disable the H/W cursor on the Bt 485.
1282:
1283: OUTPW (CRTC_INDEX, (ppdev->ExtDacCtl | 0x0200));
1284: OUTP (BT485_ADDR_CMD_REG2, ppdev->Bt485CmdReg2 & BT485_CURSOR_DISABLE);
1285:
1286: // Down load the AND mask
1287:
1288: OUTPW (CRTC_INDEX, ppdev->ExtDacCtl);
1289: OUTP (BT485_ADDR_CUR_RAM_WRITE, 0x0);
1290:
1291: OUTPW (CRTC_INDEX, (ppdev->ExtDacCtl | 0x0200));
1292:
1293: // Down load the XOR mask
1294:
1295: for (i = 0 ; i < 512 ; i++)
1296: {
1297: OUTP (BT485_CUR_RAM_ARRAY_DATA, pjXOR[i]);
1298: }
1299:
1300: // Down load the AND mask
1301:
1302: for (i = 0 ; i < 512 ; i++)
1303: {
1304: OUTP (BT485_CUR_RAM_ARRAY_DATA, pjAND[i]);
1305: }
1306:
1307: // Set the position of the cursor.
1308: // Need to do this twice, due to shadowing in the 928 and 801/805.
1309:
1310: DrvMoveBt485Pointer(pso, x, y, NULL);
1311:
1312: // Enable the H/W cursor
1313:
1314: OUTPW (CRTC_INDEX, (ppdev->ExtDacCtl | 0x0200));
1315: OUTP (BT485_ADDR_CMD_REG2, ppdev->Bt485CmdReg2 | BT485_CURSOR_MODE2);
1316:
1317: // Reset DAC extended registers.
1318:
1319: OUTPW (CRTC_INDEX, (ppdev->ExtDacCtl));
1320:
1321: return (SPS_ACCEPT_NOEXCLUDE);
1322: }
1323:
1324:
1325:
1326:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.