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; |
213 |
|
|
214 |
|
static int16 set_gamma(VidLocals *csSave, uint32 gamma) |
215 |
|
{ |
202 |
– |
return paramErr; |
203 |
– |
|
216 |
|
if (gamma == 0) { // Build linear ramp, 256 entries |
217 |
|
|
218 |
|
// Allocate new table, if necessary |
428 |
|
|
429 |
|
case cscSetHardwareCursor: { |
430 |
|
// D(bug("SetHardwareCursor\n")); |
431 |
+ |
|
432 |
+ |
if (!csSave->cursorHardware) |
433 |
+ |
return controlErr; |
434 |
+ |
|
435 |
|
csSave->cursorSet = false; |
436 |
|
bool changed = false; |
437 |
|
|
422 |
– |
// Get cursor data even on a screen, to set the right cursor image when switching back to a window |
438 |
|
// Image |
439 |
|
uint32 cursor = ReadMacInt32(param); // Pointer to CursorImage |
440 |
|
uint32 pmhandle = ReadMacInt32(cursor + ciCursorPixMap); |
441 |
|
if (pmhandle == 0 || ReadMacInt32(pmhandle) == 0) |
442 |
|
return controlErr; |
443 |
|
uint32 pixmap = ReadMacInt32(pmhandle); |
444 |
< |
if (memcmp(MacCursor + 4, Mac2HostAddr(ReadMacInt32(pixmap)), 32)) { |
445 |
< |
memcpy(MacCursor + 4, Mac2HostAddr(ReadMacInt32(pixmap)), 32); |
446 |
< |
changed = true; |
447 |
< |
} |
444 |
> |
|
445 |
> |
// XXX: only certain image formats are handled properly at the moment |
446 |
> |
uint16 rowBytes = ReadMacInt16(pixmap + 4) & 0x7FFF; |
447 |
> |
if (rowBytes != 2) |
448 |
> |
return controlErr; |
449 |
|
|
450 |
|
// Mask |
451 |
|
uint32 bmhandle = ReadMacInt32(cursor + ciCursorBitMask); |
452 |
|
if (bmhandle == 0 || ReadMacInt32(bmhandle) == 0) |
453 |
|
return controlErr; |
454 |
|
uint32 bitmap = ReadMacInt32(bmhandle); |
455 |
+ |
|
456 |
+ |
// Get cursor data even on a screen, to set the right cursor image when switching back to a window. |
457 |
+ |
// Hotspot is stale, but will be fixed by the next call to DrawHardwareCursor, which is likely to |
458 |
+ |
// occur immediately hereafter. |
459 |
+ |
|
460 |
+ |
if (memcmp(MacCursor + 4, Mac2HostAddr(ReadMacInt32(pixmap)), 32)) { |
461 |
+ |
memcpy(MacCursor + 4, Mac2HostAddr(ReadMacInt32(pixmap)), 32); |
462 |
+ |
changed = true; |
463 |
+ |
} |
464 |
|
if (memcmp(MacCursor + 4 + 32, Mac2HostAddr(ReadMacInt32(bitmap)), 32)) { |
465 |
|
memcpy(MacCursor + 4 + 32, Mac2HostAddr(ReadMacInt32(bitmap)), 32); |
466 |
|
changed = true; |
467 |
|
} |
468 |
|
|
444 |
– |
// Hotspot (!! this doesn't work) |
445 |
– |
MacCursor[2] = ReadMacInt8(0x885); |
446 |
– |
MacCursor[3] = ReadMacInt8(0x887); |
447 |
– |
|
469 |
|
// Set new cursor image |
470 |
|
if (!video_can_change_cursor()) |
471 |
|
return controlErr; |
473 |
|
video_set_cursor(); |
474 |
|
|
475 |
|
csSave->cursorSet = true; |
476 |
+ |
csSave->cursorHotFlag = true; |
477 |
|
return noErr; |
478 |
|
} |
479 |
|
|
480 |
< |
case cscDrawHardwareCursor: |
480 |
> |
case cscDrawHardwareCursor: { |
481 |
|
// D(bug("DrawHardwareCursor\n")); |
482 |
+ |
|
483 |
+ |
if (!csSave->cursorHardware) |
484 |
+ |
return controlErr; |
485 |
+ |
|
486 |
+ |
int32 oldX = csSave->cursorX; |
487 |
+ |
int32 oldY = csSave->cursorY; |
488 |
+ |
uint32 oldVisible = csSave->cursorVisible; |
489 |
+ |
|
490 |
|
csSave->cursorX = ReadMacInt32(param + csCursorX); |
491 |
|
csSave->cursorY = ReadMacInt32(param + csCursorY); |
492 |
|
csSave->cursorVisible = ReadMacInt32(param + csCursorVisible); |
493 |
+ |
bool changed = (csSave->cursorVisible != oldVisible); |
494 |
+ |
|
495 |
+ |
// If this is the first DrawHardwareCursor call since the cursor was last set (via SetHardwareCursor), |
496 |
+ |
// attempt to set an appropriate cursor hotspot. SetHardwareCursor itself does not know what the |
497 |
+ |
// hotspot should be; it knows only the cursor image and mask. The hotspot is known only to the caller, |
498 |
+ |
// and we have to try to infer it here. The usual sequence of calls when changing the cursor is: |
499 |
+ |
// |
500 |
+ |
// DrawHardwareCursor with (oldX, oldY, invisible) |
501 |
+ |
// SetHardwareCursor with (cursor) |
502 |
+ |
// DrawHardwareCursor with (newX, newY, visible) |
503 |
+ |
// |
504 |
+ |
// The key thing to note is that the sequence is intended not to change the current screen pixel location |
505 |
+ |
// indicated by the hotspot. Thus, the difference between (newX, newY) and (oldX, oldY) reflects precisely |
506 |
+ |
// the difference between the old cursor hotspot and the new one. For example, if you change from a |
507 |
+ |
// cursor whose hotspot is (1, 1) to one whose hotspot is (7, 4), then you must adjust the cursor position |
508 |
+ |
// by (-6, -3) in order for the same screen pixel to remain under the new hotspot. |
509 |
+ |
// |
510 |
+ |
// Alas, on rare occasions this heuristic can fail, and if you did nothing else you could even get stuck |
511 |
+ |
// with the wrong hotspot from then on. To address that possibility, we force the hotspot to (1, 1) |
512 |
+ |
// whenever the cursor being drawn is the standard arrow. Thus, while it is very unlikely that you will |
513 |
+ |
// ever have the wrong hotspot, if you do, it is easy to recover. |
514 |
+ |
|
515 |
+ |
if (csSave->cursorHotFlag) { |
516 |
+ |
csSave->cursorHotFlag = false; |
517 |
+ |
D(bug("old hotspot (%d, %d)\n", csSave->cursorHotX, csSave->cursorHotY)); |
518 |
+ |
|
519 |
+ |
static uint8 arrow[] = { |
520 |
+ |
0x00, 0x00, 0x40, 0x00, 0x60, 0x00, 0x70, 0x00, 0x78, 0x00, 0x7C, 0x00, 0x7E, 0x00, 0x7F, 0x00, |
521 |
+ |
0x7F, 0x80, 0x7C, 0x00, 0x6C, 0x00, 0x46, 0x00, 0x06, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, |
522 |
+ |
}; |
523 |
+ |
if (memcmp(MacCursor + 4, arrow, 32) == 0) { |
524 |
+ |
csSave->cursorHotX = 1; |
525 |
+ |
csSave->cursorHotY = 1; |
526 |
+ |
} else if (csSave->cursorX != oldX || csSave->cursorY != oldY) { |
527 |
+ |
int32 hotX = csSave->cursorHotX + (oldX - csSave->cursorX); |
528 |
+ |
int32 hotY = csSave->cursorHotY + (oldY - csSave->cursorY); |
529 |
+ |
|
530 |
+ |
if (0 <= hotX && hotX <= 15 && 0 <= hotY && hotY <= 15) { |
531 |
+ |
csSave->cursorHotX = hotX; |
532 |
+ |
csSave->cursorHotY = hotY; |
533 |
+ |
} |
534 |
+ |
} |
535 |
+ |
if (MacCursor[2] != csSave->cursorHotX || MacCursor[3] != csSave->cursorHotY) { |
536 |
+ |
MacCursor[2] = csSave->cursorHotX; |
537 |
+ |
MacCursor[3] = csSave->cursorHotY; |
538 |
+ |
changed = true; |
539 |
+ |
} |
540 |
+ |
D(bug("new hotspot (%d, %d)\n", csSave->cursorHotX, csSave->cursorHotY)); |
541 |
+ |
} |
542 |
+ |
|
543 |
+ |
if (changed && video_can_change_cursor()) |
544 |
+ |
video_set_cursor(); |
545 |
+ |
|
546 |
|
return noErr; |
547 |
+ |
} |
548 |
|
|
549 |
|
case 43: { // Driver Gestalt |
550 |
|
uint32 sel = ReadMacInt32(pb + csParam); |
601 |
|
return max; |
602 |
|
} |
603 |
|
|
604 |
+ |
// Get X/Y size of specified resolution |
605 |
+ |
static void get_size_of_resolution(int id, uint32 &x, uint32 &y) |
606 |
+ |
{ |
607 |
+ |
VideoInfo *p = VModes; |
608 |
+ |
while (p->viType != DIS_INVALID) { |
609 |
+ |
if (p->viAppleID == id) { |
610 |
+ |
x = p->viXsize; |
611 |
+ |
y = p->viYsize; |
612 |
+ |
return; |
613 |
+ |
} |
614 |
+ |
p++; |
615 |
+ |
} |
616 |
+ |
x = y = 0; |
617 |
+ |
} |
618 |
+ |
|
619 |
|
static int16 VideoStatus(uint32 pb, VidLocals *csSave) |
620 |
|
{ |
621 |
|
int16 code = ReadMacInt16(pb + csCode); |
697 |
|
case cscGetGamma: // GetGamma |
698 |
|
D(bug("GetGamma\n")); |
699 |
|
WriteMacInt32(param, (uint32)csSave->gammaTable); |
700 |
< |
return statusErr; |
700 |
> |
return noErr; |
701 |
|
|
702 |
|
case cscGetDefaultMode: // GetDefaultMode |
703 |
|
D(bug("GetDefaultMode\n")); |
807 |
|
WriteMacInt32(param + csVerticalLines, 1200); |
808 |
|
WriteMacInt32(param + csRefreshRate, 75<<16); |
809 |
|
break; |
810 |
+ |
case APPLE_CUSTOM: { |
811 |
+ |
uint32 x, y; |
812 |
+ |
get_size_of_resolution(work_id, x, y); |
813 |
+ |
WriteMacInt32(param + csHorizontalPixels, x); |
814 |
+ |
WriteMacInt32(param + csVerticalLines, y); |
815 |
+ |
WriteMacInt32(param + csRefreshRate, 75<<16); |
816 |
+ |
break; |
817 |
+ |
} |
818 |
|
} |
819 |
|
return noErr; |
820 |
|
} |
943 |
|
|
944 |
|
case cscSupportsHardwareCursor: |
945 |
|
D(bug("SupportsHardwareCursor\n")); |
946 |
< |
WriteMacInt32(param, 1); |
946 |
> |
WriteMacInt32(param, csSave->cursorHardware); |
947 |
|
return noErr; |
948 |
|
|
949 |
|
case cscGetHardwareCursorDrawState: |
950 |
|
D(bug("GetHardwareCursorDrawState\n")); |
951 |
+ |
|
952 |
+ |
if (!csSave->cursorHardware) |
953 |
+ |
return statusErr; |
954 |
+ |
|
955 |
|
WriteMacInt32(param + csCursorX, csSave->cursorX); |
956 |
|
WriteMacInt32(param + csCursorY, csSave->cursorY); |
957 |
|
WriteMacInt32(param + csCursorVisible, csSave->cursorVisible); |
996 |
|
if (private_data != NULL) { // Might be left over from a reboot |
997 |
|
if (private_data->gammaTable) |
998 |
|
Mac_sysfree(private_data->gammaTable); |
999 |
+ |
if (private_data->regEntryID) |
1000 |
+ |
Mac_sysfree(private_data->regEntryID); |
1001 |
|
} |
1002 |
|
delete private_data; |
1003 |
|
|
1039 |
|
|
1040 |
|
private_data = new VidLocals; |
1041 |
|
private_data->gammaTable = 0; |
1042 |
< |
Mac2Host_memcpy(&private_data->regEntryID, commandContents + 2, 16); // DriverInitInfo.deviceEntry |
1042 |
> |
private_data->regEntryID = Mac_sysalloc(sizeof(RegEntryID)); |
1043 |
> |
if (private_data->regEntryID == 0) { |
1044 |
> |
printf("FATAL: VideoDoDriverIO(): Can't allocate service owner\n"); |
1045 |
> |
err = -1; |
1046 |
> |
break; |
1047 |
> |
} |
1048 |
> |
Mac2Mac_memcpy(private_data->regEntryID, commandContents + 2, 16); // DriverInitInfo.deviceEntry |
1049 |
|
private_data->interruptsEnabled = false; // Disable interrupts |
1050 |
|
break; |
1051 |
|
|
1052 |
|
case kFinalizeCommand: |
1053 |
|
case kSupersededCommand: |
1054 |
< |
if (private_data != NULL && private_data->gammaTable) |
1055 |
< |
Mac_sysfree(private_data->gammaTable); |
1054 |
> |
if (private_data != NULL) { |
1055 |
> |
if (private_data->gammaTable) |
1056 |
> |
Mac_sysfree(private_data->gammaTable); |
1057 |
> |
if (private_data->regEntryID) |
1058 |
> |
Mac_sysfree(private_data->regEntryID); |
1059 |
> |
} |
1060 |
|
delete private_data; |
1061 |
|
private_data = NULL; |
1062 |
|
break; |