1 |
|
/* |
2 |
|
* video.cpp - Video/graphics emulation |
3 |
|
* |
4 |
< |
* SheepShaver (C) 1997-2004 Marc Hellwig and Christian Bauer |
4 |
> |
* SheepShaver (C) 1997-2008 Marc Hellwig and Christian Bauer |
5 |
|
* |
6 |
|
* This program is free software; you can redistribute it and/or modify |
7 |
|
* it under the terms of the GNU General Public License as published by |
21 |
|
/* |
22 |
|
* TODO |
23 |
|
* - check for supported modes ??? |
24 |
– |
* - window mode "hardware" cursor hotspot |
24 |
|
*/ |
25 |
|
|
26 |
|
#include <stdio.h> |
142 |
|
|
143 |
|
|
144 |
|
/* |
145 |
+ |
* Determine whether we should use the hardware or software cursor, and return true for the former, false for the latter. |
146 |
+ |
* Currently we use the hardware cursor if we can, but perhaps this can be made a preference someday. |
147 |
+ |
*/ |
148 |
+ |
|
149 |
+ |
static bool UseHardwareCursor(void) |
150 |
+ |
{ |
151 |
+ |
return video_can_change_cursor(); |
152 |
+ |
} |
153 |
+ |
|
154 |
+ |
|
155 |
+ |
/* |
156 |
|
* Video driver open routine |
157 |
|
*/ |
158 |
|
|
167 |
|
csSave->savePage = 0; |
168 |
|
csSave->saveVidParms = 0; // Add the right table |
169 |
|
csSave->luminanceMapping = false; |
170 |
+ |
csSave->cursorHardware = UseHardwareCursor(); |
171 |
|
csSave->cursorX = 0; |
172 |
|
csSave->cursorY = 0; |
173 |
|
csSave->cursorVisible = 0; |
174 |
|
csSave->cursorSet = 0; |
175 |
+ |
csSave->cursorHotFlag = false; |
176 |
+ |
csSave->cursorHotX = 0; |
177 |
+ |
csSave->cursorHotY = 0; |
178 |
|
|
179 |
|
// Find and set default gamma table |
180 |
|
csSave->gammaTable = 0; |
183 |
|
|
184 |
|
// Install and activate interrupt service |
185 |
|
SheepVar32 theServiceID = 0; |
186 |
< |
VSLNewInterruptService(Host2MacAddr((uint8 *)csSave->regEntryID), FOURCC('v','b','l',' '), theServiceID.addr()); |
186 |
> |
VSLNewInterruptService(csSave->regEntryID, FOURCC('v','b','l',' '), theServiceID.addr()); |
187 |
|
csSave->vslServiceID = theServiceID.value(); |
188 |
|
D(bug(" Interrupt ServiceID %08lx\n", csSave->vslServiceID)); |
189 |
|
csSave->interruptsEnabled = true; |
430 |
|
|
431 |
|
case cscSetHardwareCursor: { |
432 |
|
// D(bug("SetHardwareCursor\n")); |
433 |
+ |
|
434 |
+ |
if (!csSave->cursorHardware) |
435 |
+ |
return controlErr; |
436 |
+ |
|
437 |
|
csSave->cursorSet = false; |
438 |
|
bool changed = false; |
439 |
|
|
422 |
– |
// Get cursor data even on a screen, to set the right cursor image when switching back to a window |
440 |
|
// Image |
441 |
|
uint32 cursor = ReadMacInt32(param); // Pointer to CursorImage |
442 |
|
uint32 pmhandle = ReadMacInt32(cursor + ciCursorPixMap); |
443 |
|
if (pmhandle == 0 || ReadMacInt32(pmhandle) == 0) |
444 |
|
return controlErr; |
445 |
|
uint32 pixmap = ReadMacInt32(pmhandle); |
446 |
< |
if (memcmp(MacCursor + 4, Mac2HostAddr(ReadMacInt32(pixmap)), 32)) { |
447 |
< |
memcpy(MacCursor + 4, Mac2HostAddr(ReadMacInt32(pixmap)), 32); |
448 |
< |
changed = true; |
449 |
< |
} |
446 |
> |
|
447 |
> |
// XXX: only certain image formats are handled properly at the moment |
448 |
> |
uint16 rowBytes = ReadMacInt16(pixmap + 4) & 0x7FFF; |
449 |
> |
if (rowBytes != 2) |
450 |
> |
return controlErr; |
451 |
|
|
452 |
|
// Mask |
453 |
|
uint32 bmhandle = ReadMacInt32(cursor + ciCursorBitMask); |
454 |
|
if (bmhandle == 0 || ReadMacInt32(bmhandle) == 0) |
455 |
|
return controlErr; |
456 |
|
uint32 bitmap = ReadMacInt32(bmhandle); |
457 |
+ |
|
458 |
+ |
// Get cursor data even on a screen, to set the right cursor image when switching back to a window. |
459 |
+ |
// Hotspot is stale, but will be fixed by the next call to DrawHardwareCursor, which is likely to |
460 |
+ |
// occur immediately hereafter. |
461 |
+ |
|
462 |
+ |
if (memcmp(MacCursor + 4, Mac2HostAddr(ReadMacInt32(pixmap)), 32)) { |
463 |
+ |
memcpy(MacCursor + 4, Mac2HostAddr(ReadMacInt32(pixmap)), 32); |
464 |
+ |
changed = true; |
465 |
+ |
} |
466 |
|
if (memcmp(MacCursor + 4 + 32, Mac2HostAddr(ReadMacInt32(bitmap)), 32)) { |
467 |
|
memcpy(MacCursor + 4 + 32, Mac2HostAddr(ReadMacInt32(bitmap)), 32); |
468 |
|
changed = true; |
469 |
|
} |
470 |
|
|
444 |
– |
// Hotspot (!! this doesn't work) |
445 |
– |
MacCursor[2] = ReadMacInt8(0x885); |
446 |
– |
MacCursor[3] = ReadMacInt8(0x887); |
447 |
– |
|
471 |
|
// Set new cursor image |
472 |
|
if (!video_can_change_cursor()) |
473 |
|
return controlErr; |
475 |
|
video_set_cursor(); |
476 |
|
|
477 |
|
csSave->cursorSet = true; |
478 |
+ |
csSave->cursorHotFlag = true; |
479 |
|
return noErr; |
480 |
|
} |
481 |
|
|
482 |
< |
case cscDrawHardwareCursor: |
482 |
> |
case cscDrawHardwareCursor: { |
483 |
|
// D(bug("DrawHardwareCursor\n")); |
484 |
+ |
|
485 |
+ |
if (!csSave->cursorHardware) |
486 |
+ |
return controlErr; |
487 |
+ |
|
488 |
+ |
int32 oldX = csSave->cursorX; |
489 |
+ |
int32 oldY = csSave->cursorY; |
490 |
+ |
uint32 oldVisible = csSave->cursorVisible; |
491 |
+ |
|
492 |
|
csSave->cursorX = ReadMacInt32(param + csCursorX); |
493 |
|
csSave->cursorY = ReadMacInt32(param + csCursorY); |
494 |
|
csSave->cursorVisible = ReadMacInt32(param + csCursorVisible); |
495 |
+ |
bool changed = (csSave->cursorVisible != oldVisible); |
496 |
+ |
|
497 |
+ |
// If this is the first DrawHardwareCursor call since the cursor was last set (via SetHardwareCursor), |
498 |
+ |
// attempt to set an appropriate cursor hotspot. SetHardwareCursor itself does not know what the |
499 |
+ |
// hotspot should be; it knows only the cursor image and mask. The hotspot is known only to the caller, |
500 |
+ |
// and we have to try to infer it here. The usual sequence of calls when changing the cursor is: |
501 |
+ |
// |
502 |
+ |
// DrawHardwareCursor with (oldX, oldY, invisible) |
503 |
+ |
// SetHardwareCursor with (cursor) |
504 |
+ |
// DrawHardwareCursor with (newX, newY, visible) |
505 |
+ |
// |
506 |
+ |
// The key thing to note is that the sequence is intended not to change the current screen pixel location |
507 |
+ |
// indicated by the hotspot. Thus, the difference between (newX, newY) and (oldX, oldY) reflects precisely |
508 |
+ |
// the difference between the old cursor hotspot and the new one. For example, if you change from a |
509 |
+ |
// cursor whose hotspot is (1, 1) to one whose hotspot is (7, 4), then you must adjust the cursor position |
510 |
+ |
// by (-6, -3) in order for the same screen pixel to remain under the new hotspot. |
511 |
+ |
// |
512 |
+ |
// Alas, on rare occasions this heuristic can fail, and if you did nothing else you could even get stuck |
513 |
+ |
// with the wrong hotspot from then on. To address that possibility, we force the hotspot to (1, 1) |
514 |
+ |
// whenever the cursor being drawn is the standard arrow. Thus, while it is very unlikely that you will |
515 |
+ |
// ever have the wrong hotspot, if you do, it is easy to recover. |
516 |
+ |
|
517 |
+ |
if (csSave->cursorHotFlag) { |
518 |
+ |
csSave->cursorHotFlag = false; |
519 |
+ |
D(bug("old hotspot (%d, %d)\n", csSave->cursorHotX, csSave->cursorHotY)); |
520 |
+ |
|
521 |
+ |
static uint8 arrow[] = { |
522 |
+ |
0x00, 0x00, 0x40, 0x00, 0x60, 0x00, 0x70, 0x00, 0x78, 0x00, 0x7C, 0x00, 0x7E, 0x00, 0x7F, 0x00, |
523 |
+ |
0x7F, 0x80, 0x7C, 0x00, 0x6C, 0x00, 0x46, 0x00, 0x06, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, |
524 |
+ |
}; |
525 |
+ |
if (memcmp(MacCursor + 4, arrow, 32) == 0) { |
526 |
+ |
csSave->cursorHotX = 1; |
527 |
+ |
csSave->cursorHotY = 1; |
528 |
+ |
} else if (csSave->cursorX != oldX || csSave->cursorY != oldY) { |
529 |
+ |
int32 hotX = csSave->cursorHotX + (oldX - csSave->cursorX); |
530 |
+ |
int32 hotY = csSave->cursorHotY + (oldY - csSave->cursorY); |
531 |
+ |
|
532 |
+ |
if (0 <= hotX && hotX <= 15 && 0 <= hotY && hotY <= 15) { |
533 |
+ |
csSave->cursorHotX = hotX; |
534 |
+ |
csSave->cursorHotY = hotY; |
535 |
+ |
} |
536 |
+ |
} |
537 |
+ |
if (MacCursor[2] != csSave->cursorHotX || MacCursor[3] != csSave->cursorHotY) { |
538 |
+ |
MacCursor[2] = csSave->cursorHotX; |
539 |
+ |
MacCursor[3] = csSave->cursorHotY; |
540 |
+ |
changed = true; |
541 |
+ |
} |
542 |
+ |
D(bug("new hotspot (%d, %d)\n", csSave->cursorHotX, csSave->cursorHotY)); |
543 |
+ |
} |
544 |
+ |
|
545 |
+ |
if (changed && video_can_change_cursor()) |
546 |
+ |
video_set_cursor(); |
547 |
+ |
|
548 |
|
return noErr; |
549 |
+ |
} |
550 |
|
|
551 |
|
case 43: { // Driver Gestalt |
552 |
|
uint32 sel = ReadMacInt32(pb + csParam); |
603 |
|
return max; |
604 |
|
} |
605 |
|
|
606 |
+ |
// Get X/Y size of specified resolution |
607 |
+ |
static void get_size_of_resolution(int id, uint32 &x, uint32 &y) |
608 |
+ |
{ |
609 |
+ |
VideoInfo *p = VModes; |
610 |
+ |
while (p->viType != DIS_INVALID) { |
611 |
+ |
if (p->viAppleID == id) { |
612 |
+ |
x = p->viXsize; |
613 |
+ |
y = p->viYsize; |
614 |
+ |
return; |
615 |
+ |
} |
616 |
+ |
p++; |
617 |
+ |
} |
618 |
+ |
x = y = 0; |
619 |
+ |
} |
620 |
+ |
|
621 |
|
static int16 VideoStatus(uint32 pb, VidLocals *csSave) |
622 |
|
{ |
623 |
|
int16 code = ReadMacInt16(pb + csCode); |
809 |
|
WriteMacInt32(param + csVerticalLines, 1200); |
810 |
|
WriteMacInt32(param + csRefreshRate, 75<<16); |
811 |
|
break; |
812 |
+ |
case APPLE_CUSTOM: { |
813 |
+ |
uint32 x, y; |
814 |
+ |
get_size_of_resolution(work_id, x, y); |
815 |
+ |
WriteMacInt32(param + csHorizontalPixels, x); |
816 |
+ |
WriteMacInt32(param + csVerticalLines, y); |
817 |
+ |
WriteMacInt32(param + csRefreshRate, 75<<16); |
818 |
+ |
break; |
819 |
+ |
} |
820 |
|
} |
821 |
|
return noErr; |
822 |
|
} |
945 |
|
|
946 |
|
case cscSupportsHardwareCursor: |
947 |
|
D(bug("SupportsHardwareCursor\n")); |
948 |
< |
WriteMacInt32(param, 1); |
948 |
> |
WriteMacInt32(param, csSave->cursorHardware); |
949 |
|
return noErr; |
950 |
|
|
951 |
|
case cscGetHardwareCursorDrawState: |
952 |
|
D(bug("GetHardwareCursorDrawState\n")); |
953 |
+ |
|
954 |
+ |
if (!csSave->cursorHardware) |
955 |
+ |
return statusErr; |
956 |
+ |
|
957 |
|
WriteMacInt32(param + csCursorX, csSave->cursorX); |
958 |
|
WriteMacInt32(param + csCursorY, csSave->cursorY); |
959 |
|
WriteMacInt32(param + csCursorVisible, csSave->cursorVisible); |
998 |
|
if (private_data != NULL) { // Might be left over from a reboot |
999 |
|
if (private_data->gammaTable) |
1000 |
|
Mac_sysfree(private_data->gammaTable); |
1001 |
+ |
if (private_data->regEntryID) |
1002 |
+ |
Mac_sysfree(private_data->regEntryID); |
1003 |
|
} |
1004 |
|
delete private_data; |
1005 |
|
|
1041 |
|
|
1042 |
|
private_data = new VidLocals; |
1043 |
|
private_data->gammaTable = 0; |
1044 |
< |
Mac2Host_memcpy(&private_data->regEntryID, commandContents + 2, 16); // DriverInitInfo.deviceEntry |
1044 |
> |
private_data->regEntryID = Mac_sysalloc(sizeof(RegEntryID)); |
1045 |
> |
if (private_data->regEntryID == 0) { |
1046 |
> |
printf("FATAL: VideoDoDriverIO(): Can't allocate service owner\n"); |
1047 |
> |
err = -1; |
1048 |
> |
break; |
1049 |
> |
} |
1050 |
> |
Mac2Mac_memcpy(private_data->regEntryID, commandContents + 2, 16); // DriverInitInfo.deviceEntry |
1051 |
|
private_data->interruptsEnabled = false; // Disable interrupts |
1052 |
|
break; |
1053 |
|
|
1054 |
|
case kFinalizeCommand: |
1055 |
|
case kSupersededCommand: |
1056 |
< |
if (private_data != NULL && private_data->gammaTable) |
1057 |
< |
Mac_sysfree(private_data->gammaTable); |
1056 |
> |
if (private_data != NULL) { |
1057 |
> |
if (private_data->gammaTable) |
1058 |
> |
Mac_sysfree(private_data->gammaTable); |
1059 |
> |
if (private_data->regEntryID) |
1060 |
> |
Mac_sysfree(private_data->regEntryID); |
1061 |
> |
} |
1062 |
|
delete private_data; |
1063 |
|
private_data = NULL; |
1064 |
|
break; |