ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/video_macosx.mm
Revision: 1.4
Committed: 2002-06-05T10:11:40Z (22 years, 6 months ago) by nigel
Branch: MAIN
Changes since 1.3: +87 -37 lines
Log Message:
Sequencing changes in init_screen(), removed some global vars and functions,
assorted tidyup, big changes to video_switch_to_mode()

File Contents

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