ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/video_macosx.mm
Revision: 1.3
Committed: 2002-05-30T12:43:32Z (22 years, 2 months ago) by nigel
Branch: MAIN
Changes since 1.2: +84 -59 lines
Log Message:
Removed duplicate modes, new way of checking fullscreen mode, use sheets
instead of panels for warnings & errors

File Contents

# Content
1 /*
2 * $Id: video_macosx.mm,v 1.2 2002/04/05 12:15:34 nigel Exp $
3 *
4 * video_macosx.mm - Interface between Basilisk II and Cocoa windowing.
5 * Based on video_amiga.cpp and video_x.cpp
6 *
7 * Basilisk II (C) 1997-2002 Christian Bauer
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24
25 #include "sysdeps.h"
26
27 #ifdef HAVE_PTHREADS
28 # include <pthread.h>
29 #endif
30
31 #include <adb.h>
32 #include <cpu_emulation.h>
33 #include <main.h>
34 #include "macos_util_macosx.h"
35 #include <prefs.h>
36 #include <user_strings.h>
37 #include "video_macosx.h"
38
39 #define DEBUG 0
40 #include "debug.h"
41
42 #import <Foundation/NSString.h> // Needed for NSLog(@"")
43 #import "misc_macosx.h" // WarningSheet() prototype
44
45
46
47 // Global variables
48 uint8 display_type = DISPLAY_WINDOW, // These are used by PrefsEditor
49 frame_skip;
50 uint16 init_width = MIN_WIDTH, // as well as this code
51 init_height = MIN_HEIGHT,
52 init_depth = 32,
53 screen_height = 0; // Used by processMouseMove:
54
55 EmulatorView *output = nil; // Set by [EmulatorView init]
56 NSWindow *the_win = nil; // Set by [Emulator awakeFromNib]
57 static void *the_buffer = NULL;
58
59
60 #ifdef CGIMAGEREF
61 static CGImageRef imageRef = nil;
62 #endif
63
64 #ifdef NSBITMAP
65 #import <AppKit/NSBitmapImageRep.h>
66
67 static NSBitmapImageRep *bitmap = nil;
68 #endif
69
70 // These record changes we made in setting full screen mode
71 static CGDirectDisplayID theDisplay = NULL;
72 static CFDictionaryRef originalMode = NULL,
73 newMode = NULL;
74
75
76
77 // Prototypes
78
79 static void add_mode (const uint16 width, const uint16 height,
80 const uint32 resolution_id,
81 const uint32 bytes_per_row,
82 const video_depth depth);
83 static void add_standard_modes (const video_depth depth);
84
85 static bool video_open (const video_mode &mode);
86 static void video_close (void);
87
88
89
90 /*
91 * Utility functions
92 */
93
94 uint8 bits_from_depth(const video_depth depth)
95 {
96 int bits = 1 << depth;
97 // if (bits == 16)
98 // bits = 15;
99 // else if (bits == 32)
100 // bits = 24;
101 return bits;
102 }
103
104 char *
105 colours_from_depth(const video_depth depth)
106 {
107 switch ( depth )
108 {
109 case VDEPTH_1BIT : return "Monochrome";
110 case VDEPTH_2BIT : return "4 colours";
111 case VDEPTH_4BIT : return "16 colours";
112 case VDEPTH_8BIT : return "256 colours";
113 case VDEPTH_16BIT: return "Thousands of colours";
114 case VDEPTH_32BIT: return "Millions of colours";
115 }
116
117 return "illegal colour depth";
118 }
119
120 char *
121 colours_from_depth(const uint16 depth)
122 {
123 return colours_from_depth(DepthModeForPixelDepth(depth) );
124 }
125
126 bool
127 parse_screen_prefs(const char *mode_str)
128 {
129 if (sscanf(mode_str, "win/%hd/%hd/%hd",
130 &init_width, &init_height, &init_depth) == 3)
131 display_type = DISPLAY_WINDOW;
132 else if (sscanf(mode_str, "win/%hd/%hd", &init_width, &init_height) == 2)
133 display_type = DISPLAY_WINDOW;
134 else if (strcmp(mode_str, "full") == 0)
135 display_type = DISPLAY_SCREEN;
136 else if (sscanf(mode_str, "full/%hd/%hd/%hd",
137 &init_width, &init_height, &init_depth) == 3)
138 display_type = DISPLAY_SCREEN;
139 else if (sscanf(mode_str, "full/%hd/%hd", &init_width, &init_height) == 2)
140 display_type = DISPLAY_SCREEN;
141 else if (sscanf(mode_str, "opengl/%hd/%hd/%hd",
142 &init_width, &init_height, &init_depth) == 3)
143 display_type = DISPLAY_OPENGL;
144 else if (sscanf(mode_str, "opengl/%hd/%hd", &init_width, &init_height) == 2)
145 display_type = DISPLAY_OPENGL;
146 else return false;
147
148 return true;
149 }
150
151
152 // Add mode to list of supported modes
153 static void
154 add_mode(const uint16 width, const uint16 height,
155 const uint32 resolution_id, const uint32 bytes_per_row,
156 const video_depth depth)
157 {
158 vector<video_mode>::const_iterator i,
159 end = VideoModes.end();
160
161 for (i = VideoModes.begin(); i != end; ++i)
162 if ( i->x == width && i->y == height &&
163 i->bytes_per_row == bytes_per_row && i->depth == depth )
164 {
165 D(NSLog(@"Duplicate mode (%hdx%hdx%ld, ID %02x, new ID %02x)\n",
166 width, height, depth, i->resolution_id, resolution_id));
167 return;
168 }
169
170 video_mode mode;
171
172 mode.x = width;
173 mode.y = height;
174 mode.resolution_id = resolution_id;
175 mode.bytes_per_row = bytes_per_row;
176 mode.depth = depth;
177
178 D(bug("Added video mode: w=%d h=%d d=%d(%d bits)\n",
179 width, height, depth, bits_from_depth(depth) ));
180
181 VideoModes.push_back(mode);
182 }
183
184 // Add standard list of windowed modes for given color depth
185 static void add_standard_modes(const video_depth depth)
186 {
187 D(bug("add_standard_modes: depth=%d(%d bits)\n",
188 depth, bits_from_depth(depth) ));
189
190 add_mode(512, 384, 0x80, TrivialBytesPerRow(512, depth), depth);
191 add_mode(640, 480, 0x81, TrivialBytesPerRow(640, depth), depth);
192 add_mode(800, 600, 0x82, TrivialBytesPerRow(800, depth), depth);
193 add_mode(832, 624, 0x83, TrivialBytesPerRow(832, depth), depth);
194 add_mode(1024, 768, 0x84, TrivialBytesPerRow(1024, depth), depth);
195 add_mode(1152, 768, 0x85, TrivialBytesPerRow(1152, depth), depth);
196 add_mode(1152, 870, 0x86, TrivialBytesPerRow(1152, depth), depth);
197 add_mode(1280, 1024, 0x87, TrivialBytesPerRow(1280, depth), depth);
198 add_mode(1600, 1200, 0x88, TrivialBytesPerRow(1600, depth), depth);
199 }
200
201 // Helper function to get a 32bit int from a dictionary
202 static int32 getCFint32 (CFDictionaryRef dict, CFStringRef key)
203 {
204 CFNumberRef ref = CFDictionaryGetValue(dict, key);
205
206 if ( ref )
207 {
208 int32 val;
209
210 if ( CFNumberGetValue(ref, kCFNumberSInt32Type, &val) )
211 return val;
212 else
213 NSLog(@"getCFint32() - Failed to get the value %@", key);
214 }
215 else
216 NSLog(@"getCFint32() - Failed to get a 32bit int for %@", key);
217
218 return 0;
219 }
220
221 static bool add_CGDirectDisplay_modes()
222 {
223 #define kMaxDisplays 8
224 CGDirectDisplayID displays[kMaxDisplays];
225 CGDisplayErr err;
226 CGDisplayCount n;
227 int32 oldRes = 0,
228 res_id = 0x80;
229
230
231 err = CGGetActiveDisplayList(kMaxDisplays, displays, &n);
232 if ( err != CGDisplayNoErr )
233 n = 1, displays[n] = kCGDirectMainDisplay;
234
235 for ( CGDisplayCount dc = 0; dc < n; ++dc )
236 {
237 CGDirectDisplayID d = displays[dc];
238 CFArrayRef m = CGDisplayAvailableModes(d);
239
240 if ( m == NULL ) // Store the current display mode
241 add_mode(CGDisplayPixelsWide(d),
242 CGDisplayPixelsHigh(d),
243 res_id++, CGDisplayBytesPerRow(d),
244 DepthModeForPixelDepth(CGDisplayBitsPerPixel(d)));
245 else
246 {
247 CFIndex nModes = CFArrayGetCount(m);
248
249 for ( CFIndex mc = 0; mc < nModes; ++mc )
250 {
251 CFDictionaryRef modeSpec = CFArrayGetValueAtIndex(m, mc);
252
253 int32 bpp = getCFint32(modeSpec, kCGDisplayBitsPerPixel);
254 int32 height = getCFint32(modeSpec, kCGDisplayHeight);
255 int32 width = getCFint32(modeSpec, kCGDisplayWidth);
256 video_depth depth = DepthModeForPixelDepth(bpp);
257
258 if ( ! bpp || ! height || ! width )
259 {
260 NSLog(@"Could not get details of mode %d, display %d",
261 mc, dc);
262 return false;
263 }
264 #if VERBOSE
265 else
266 NSLog(@"Display %ld, spec = %@", d, modeSpec);
267 #endif
268
269 if ( ! oldRes )
270 oldRes = width * height;
271 else
272 if ( oldRes != width * height )
273 {
274 oldRes = width * height;
275 ++res_id;
276 }
277
278 add_mode(width, height, res_id, 0, depth);
279 }
280 }
281 }
282
283 return true;
284 }
285
286 // Set Mac frame layout and base address (uses the_buffer/MacFrameBaseMac)
287 static void set_mac_frame_buffer(const video_depth depth)
288 {
289 #if !REAL_ADDRESSING && !DIRECT_ADDRESSING
290 switch ( depth )
291 {
292 // case VDEPTH_15BIT:
293 case VDEPTH_16BIT: MacFrameLayout = FLAYOUT_HOST_555; break;
294 // case VDEPTH_24BIT:
295 case VDEPTH_32BIT: MacFrameLayout = FLAYOUT_HOST_888; break;
296 default : MacFrameLayout = FLAYOUT_DIRECT;
297 }
298 VideoMonitor.mac_frame_base = MacFrameBaseMac;
299
300 // Set variables used by UAE memory banking
301 MacFrameBaseHost = the_buffer;
302 MacFrameSize = VideoMonitor.mode.bytes_per_row * VideoMonitor.mode.y;
303 InitFrameBufferMapping();
304 #else
305 VideoMonitor.mac_frame_base = Host2MacAddr(the_buffer);
306 #endif
307 D(bug("VideoMonitor.mac_frame_base = %08x\n", VideoMonitor.mac_frame_base));
308 }
309
310 void resizeWinBy(const short deltaX, const short deltaY)
311 {
312 NSRect rect = [the_win frame];
313
314 D(bug("resizeWinBy(%d,%d) - ", deltaX, deltaY));
315 D(bug("old x=%g, y=%g", rect.size.width, rect.size.height));
316
317 rect.size.width += deltaX;
318 rect.size.height += deltaY;
319
320 D(bug(", new x=%g, y=%g\n", rect.size.width, rect.size.height));
321
322 [the_win setFrame: rect display: YES];
323 rect = [the_win frame];
324 }
325
326 void resizeWinTo(const uint16 newWidth, const uint16 newHeight)
327 {
328 int deltaX = newWidth - [output width],
329 deltaY = newHeight - [output height];
330
331 D(bug("resizeWinTo(%d,%d)\n", newWidth, newHeight));
332
333 if ( deltaX || deltaY )
334 resizeWinBy(deltaX, deltaY);
335 }
336
337 // Open window
338 static bool init_window(const video_mode &mode)
339 {
340 #ifdef CGIMAGEREF
341 CGColorSpaceRef colourSpace;
342 CGDataProviderRef provider;
343 #endif
344 short bitsPer, samplesPer; // How big is each Pixel?
345 int the_buffer_size;
346
347 D(bug("init_window: depth=%d(%d bits)\n",
348 mode.depth, bits_from_depth(mode.depth) ));
349
350
351 // Set absolute mouse mode
352 ADBSetRelMouseMode(false);
353
354
355 // Open window
356 if (the_win == NULL)
357 {
358 ErrorAlert(STR_OPEN_WINDOW_ERR);
359 return false;
360 }
361 resizeWinTo(mode.x, mode.y);
362
363
364 // Create frame buffer ("height + 2" for safety)
365 the_buffer_size = mode.bytes_per_row * (mode.y + 2);
366 the_buffer = calloc(the_buffer_size, 1);
367 if (the_buffer == NULL)
368 {
369 NSLog(@"calloc(%d) failed", the_buffer_size);
370 ErrorAlert(STR_NO_MEM_ERR);
371 return false;
372 }
373 D(bug("the_buffer = %p\n", the_buffer));
374
375
376 if ( mode.depth == VDEPTH_1BIT )
377 bitsPer = 1;
378 else
379 bitsPer = 8;
380
381 if ( mode.depth == VDEPTH_32BIT )
382 samplesPer = 3;
383 else
384 samplesPer = 1;
385
386 #ifdef CGIMAGEREF
387 switch ( mode.depth )
388 {
389 //case VDEPTH_1BIT: colourSpace = CGColorSpaceCreateDeviceMono(); break
390 case VDEPTH_8BIT: colourSpace = CGColorSpaceCreateDeviceGray(); break;
391 case VDEPTH_32BIT: colourSpace = CGColorSpaceCreateDeviceRGB(); break;
392 default: colourSpace = NULL;
393 }
394
395 if ( ! colourSpace )
396 {
397 ErrorAlert("No valid colour space");
398 return false;
399 }
400
401 provider = CGDataProviderCreateWithData(NULL, the_buffer,
402 the_buffer_size, NULL);
403 if ( ! provider )
404 {
405 ErrorAlert("Could not create CGDataProvider from buffer data");
406 return false;
407 }
408 imageRef = CGImageCreate(mode.x,
409 mode.y,
410 bitsPer,
411 bits_from_depth(mode.depth),
412 mode.bytes_per_row,
413 colourSpace,
414 kCGImageAlphaNoneSkipFirst,
415 provider,
416 NULL, // colourMap
417 NO, // shouldInterpolate
418 kCGRenderingIntentDefault);
419 if ( ! imageRef )
420 {
421 ErrorAlert("Could not create CGImage from CGDataProvider");
422 return false;
423 }
424 CGDataProviderRelease(provider);
425 CGColorSpaceRelease(colourSpace);
426
427 [output readyToDraw: imageRef
428 imageWidth: mode.x
429 imageHeight: mode.y];
430 #else
431 unsigned char *offsetBuffer = the_buffer;
432 offsetBuffer += 1; // OS X NSBitmaps are RGBA, but Basilisk generates ARGB
433 #endif
434
435 #ifdef NSBITMAP
436 bitmap = [NSBitmapImageRep alloc];
437 bitmap = [bitmap initWithBitmapDataPlanes: (unsigned char **) &offsetBuffer
438 pixelsWide: mode.x
439 pixelsHigh: mode.y
440 bitsPerSample: bitsPer
441 samplesPerPixel: samplesPer
442 hasAlpha: NO
443 isPlanar: NO
444 colorSpaceName: NSCalibratedRGBColorSpace
445 bytesPerRow: mode.bytes_per_row
446 bitsPerPixel: bits_from_depth(mode.depth)];
447
448 if ( bitmap == nil )
449 {
450 ErrorAlert("Could not allocate an NSBitmapImageRep");
451 return false;
452 }
453
454 [output readyToDraw: bitmap
455 imageWidth: mode.x
456 imageHeight: mode.y];
457 #endif
458
459 #ifdef CGDRAWBITMAP
460 [output readyToDraw: offsetBuffer
461 width: mode.x
462 height: mode.y
463 bps: bitsPer
464 spp: samplesPer
465 bpp: bits_from_depth(mode.depth)
466 bpr: mode.bytes_per_row
467 isPlanar: NO
468 hasAlpha: NO];
469 #endif
470
471 // Set VideoMonitor
472 VideoMonitor.mode = mode;
473 set_mac_frame_buffer(mode.depth);
474
475 return true;
476 }
477
478 #import <AppKit/NSEvent.h>
479 #import <Carbon/Carbon.h>
480
481 static bool init_screen(video_mode &mode)
482 {
483 // Set absolute mouse mode
484 ADBSetRelMouseMode(false);
485
486 theDisplay = kCGDirectMainDisplay; // For now
487
488 originalMode = CGDisplayCurrentMode(theDisplay);
489 if ( nil == originalMode )
490 {
491 ErrorSheet(@"Could not get current mode of display", the_win);
492 return false;
493 }
494
495 D(NSLog(@"About to call CGDisplayBestModeForParameters()"));
496 newMode = CGDisplayBestModeForParameters(theDisplay,
497 bits_from_depth(mode.depth),
498 mode.x, mode.y, NULL);
499 if ( NULL == newMode )
500 {
501 ErrorSheet(@"Could not find a matching screen mode", the_win);
502 return false;
503 }
504
505 // This sometimes takes ages to return after the window is genied,
506 // so for now we leave it onscreen
507 // [the_win miniaturize: nil];
508
509 D(NSLog(@"About to call CGDisplayCapture()"));
510 if ( CGDisplayCapture(theDisplay) != CGDisplayNoErr )
511 {
512 // [the_win deminiaturize: nil];
513 ErrorSheet(@"Could not capture display", the_win);
514 return false;
515 }
516
517 // Set screen height for mouse co-ordinate flipping
518 if ( ! screen_height )
519 screen_height = CGDisplayPixelsHigh(theDisplay);
520
521 D(NSLog(@"About to call CGDisplaySwitchToMode()"));
522 if ( CGDisplaySwitchToMode(theDisplay, newMode) != CGDisplayNoErr )
523 {
524 // [the_win deminiaturize: nil];
525 ErrorSheet(@"Could not switch to matching screen mode", the_win);
526 return false;
527 }
528
529 if ( mode.bytes_per_row != CGDisplayBytesPerRow(theDisplay) )
530 {
531 D(bug("Bytes per row (%d) doesn't match current (%ld)\n",
532 mode.bytes_per_row, CGDisplayBytesPerRow(theDisplay)));
533 mode.bytes_per_row = CGDisplayBytesPerRow(theDisplay);
534 }
535
536 HideMenuBar();
537 CGDisplayHideCursor(theDisplay);
538
539 the_buffer = CGDisplayBaseAddress(theDisplay);
540 if ( the_buffer == NULL )
541 {
542 video_close();
543 ErrorSheet(@"Could not get base address of screen", the_win);
544 return false;
545 }
546
547 // Send emulated mouse to current location
548 NSPoint mouse = [NSEvent mouseLocation];
549 ADBMouseMoved((int)mouse.x, screen_height - (int)mouse.y);
550 //[output performSelector: @selector(processMouseMove:)
551 // withObject: nil
552 // afterDelay: 10.0];
553
554 // Set VideoMonitor
555 VideoMonitor.mode = mode;
556 set_mac_frame_buffer(mode.depth);
557
558 [output startedFullScreen]; // For [Controller sendEvent:]
559
560 return true;
561 }
562
563 static bool init_opengl(const video_mode &mode)
564 {
565 ErrorAlert("Sorry. OpenGL mode is not implemented yet");
566 return false;
567 }
568
569 /*
570 * Initialization
571 */
572
573 bool VideoInit(bool classic)
574 {
575 // Read frame skip prefs
576 frame_skip = PrefsFindInt32("frameskip");
577 if (frame_skip == 0)
578 frame_skip = 1;
579
580 // Get screen mode from preferences
581 const char *mode_str;
582 if (classic)
583 mode_str = "win/512/342";
584 else
585 mode_str = PrefsFindString("screen");
586
587 // Determine display_type and init_width, height & depth
588 parse_screen_prefs(mode_str);
589
590 // Construct list of supported modes
591 if (classic)
592 add_mode(512, 342, 0x80, 64, VDEPTH_1BIT);
593 else
594 switch ( display_type )
595 {
596 case DISPLAY_SCREEN:
597 if ( ! add_CGDirectDisplay_modes() )
598 {
599 ErrorAlert("Unable to get list of displays for full screen mode");
600 return false;
601 }
602 break;
603 case DISPLAY_OPENGL:
604 // Same as window depths and sizes?
605 case DISPLAY_WINDOW:
606 //add_standard_modes(VDEPTH_1BIT);
607 //add_standard_modes(VDEPTH_8BIT);
608 //add_standard_modes(VDEPTH_16BIT);
609 add_standard_modes(VDEPTH_32BIT);
610 break;
611 }
612
613 video_init_depth_list();
614
615 #if DEBUG
616 bug("Available video modes:\n");
617 vector<video_mode>::const_iterator i, end = VideoModes.end();
618 for (i = VideoModes.begin(); i != end; ++i)
619 bug(" %dx%d (ID %02x), %s\n", i->x, i->y, i->resolution_id,
620 colours_from_depth(i->depth));
621 #endif
622
623 D(bug("VideoInit: width=%hd height=%hd depth=%d\n",
624 init_width, init_height, init_depth));
625
626 // Find requested default mode and open display
627 if (VideoModes.size() > 0)
628 {
629 // Find mode with specified dimensions
630 std::vector<video_mode>::const_iterator i, end = VideoModes.end();
631 for (i = VideoModes.begin(); i != end; ++i)
632 {
633 D(bug("VideoInit: w=%d h=%d d=%d\n",
634 i->x, i->y, bits_from_depth(i->depth)));
635 if (i->x == init_width && i->y == init_height
636 && bits_from_depth(i->depth) == init_depth)
637 return video_open(*i);
638 }
639 }
640
641 char str[150];
642 sprintf(str, "Cannot open selected video mode\r(%hd x %hd, %s).\r%s",
643 init_width, init_height,
644 colours_from_depth(init_depth), "Using lowest resolution");
645 WarningAlert(str);
646
647 return video_open(VideoModes[0]);
648 }
649
650
651 // Open display for specified mode
652 static bool video_open(const video_mode &mode)
653 {
654 D(bug("video_open: width=%d height=%d depth=%d bytes_per_row=%d\n",
655 mode.x, mode.y, bits_from_depth(mode.depth), mode.bytes_per_row));
656
657 // Open display
658 switch ( display_type )
659 {
660 case DISPLAY_WINDOW: return init_window(mode);
661 case DISPLAY_SCREEN: return init_screen((video_mode &)mode);
662 case DISPLAY_OPENGL: return init_opengl(mode);
663 }
664
665 return false;
666 }
667
668
669 static void video_close()
670 {
671 D(bug("video_close()\n"));
672
673 switch ( display_type ) {
674 case DISPLAY_WINDOW:
675 // Stop redraw thread
676 [output disableDrawing];
677
678 // Free frame buffer stuff
679 #ifdef CGIMAGEREF
680 CGImageRelease(imageRef);
681 #endif
682 #ifdef NSBITMAP
683 [bitmap release];
684 #endif
685 free(the_buffer);
686
687 break;
688
689 case DISPLAY_SCREEN:
690 if ( theDisplay && originalMode )
691 {
692 CGDisplayShowCursor(theDisplay);
693 ShowMenuBar();
694 CGDisplaySwitchToMode(theDisplay, originalMode);
695 CGDisplayRelease(theDisplay);
696 //[the_win deminiaturize: nil];
697 }
698 break;
699
700 case DISPLAY_OPENGL:
701 break;
702 }
703 }
704
705
706 /*
707 * Deinitialization
708 */
709
710 void VideoExit(void)
711 {
712 video_close();
713 }
714
715
716 /*
717 * Set palette
718 */
719
720 void video_set_palette(uint8 *pal, int num)
721 {
722 if ( [output isFullScreen] && CGDisplayCanSetPalette(theDisplay)
723 && ! IsDirectMode(VideoMonitor.mode) )
724 {
725 CGDirectPaletteRef CGpal;
726 CGDisplayErr err;
727
728
729 CGpal = CGPaletteCreateWithByteSamples((CGDeviceByteColor *)pal, num);
730 err = CGDisplaySetPalette(theDisplay, CGpal);
731 if ( err != noErr )
732 NSLog(@"Failed to set palette, error = %d", err);
733 CGPaletteRelease(CGpal);
734 }
735 }
736
737
738 /*
739 * Switch video mode
740 */
741
742 void video_switch_to_mode(const video_mode &mode)
743 {
744 // Close and reopen display
745 video_close();
746 if (!video_open(mode))
747 {
748 if ( display_type == DISPLAY_SCREEN )
749 ErrorAlert("Cannot switch screen to selected video mode");
750 else
751 ErrorAlert(STR_OPEN_WINDOW_ERR);
752 QuitEmulator();
753 }
754 }
755
756 /*
757 * Close down full-screen mode
758 * (if bringing up error alerts is unsafe while in full-screen mode)
759 */
760
761 void VideoQuitFullScreen(void)
762 {
763 }
764
765
766 /*
767 * Mac VBL interrupt
768 */
769
770 void VideoInterrupt(void)
771 {
772 }
773
774
775 // This function is called on non-threaded platforms from a timer interrupt
776 void VideoRefresh(void)
777 {
778 }