ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/video_macosx.mm
Revision: 1.13
Committed: 2004-01-27T04:39:33Z (20 years, 10 months ago) by nigel
Branch: MAIN
Changes since 1.12: +4 -1 lines
Log Message:
On 10.1, parse_screen_prefs() was being called with a nil string
(i.e. PrefsInit wasn't getting a screen pref item). Cope with that.

File Contents

# User Rev Content
1 nigel 1.1 /*
2 nigel 1.13 * $Id: video_macosx.mm,v 1.12 2004/01/20 23:41:11 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 cebix 1.11 * Basilisk II (C) 1997-2004 Christian Bauer
8 nigel 1.1 *
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 nigel 1.6 #define VERBOSE 0
41 nigel 1.1 #include "debug.h"
42    
43 nigel 1.5 #ifdef NSBITMAP
44     #import <AppKit/NSBitmapImageRep.h>
45     #endif
46    
47 nigel 1.3 #import <Foundation/NSString.h> // Needed for NSLog(@"")
48     #import "misc_macosx.h" // WarningSheet() prototype
49    
50 nigel 1.1
51    
52     // Global variables
53     uint8 display_type = DISPLAY_WINDOW, // These are used by PrefsEditor
54     frame_skip;
55     uint16 init_width = MIN_WIDTH, // as well as this code
56     init_height = MIN_HEIGHT,
57 nigel 1.4 init_depth = 32;
58 nigel 1.1
59     EmulatorView *output = nil; // Set by [EmulatorView init]
60     NSWindow *the_win = nil; // Set by [Emulator awakeFromNib]
61    
62 nigel 1.5 static BOOL singleDisplay = YES;
63 nigel 1.1
64     /*
65     * Utility functions
66     */
67    
68 nigel 1.4 static uint8
69     bits_from_depth(const video_depth depth)
70 nigel 1.1 {
71     int bits = 1 << depth;
72     // if (bits == 16)
73     // bits = 15;
74     // else if (bits == 32)
75     // bits = 24;
76     return bits;
77     }
78    
79 nigel 1.4 static char *
80 nigel 1.1 colours_from_depth(const video_depth depth)
81     {
82     switch ( depth )
83     {
84     case VDEPTH_1BIT : return "Monochrome";
85     case VDEPTH_2BIT : return "4 colours";
86     case VDEPTH_4BIT : return "16 colours";
87     case VDEPTH_8BIT : return "256 colours";
88     case VDEPTH_16BIT: return "Thousands of colours";
89     case VDEPTH_32BIT: return "Millions of colours";
90     }
91    
92     return "illegal colour depth";
93     }
94    
95 nigel 1.4 static char *
96 nigel 1.1 colours_from_depth(const uint16 depth)
97     {
98     return colours_from_depth(DepthModeForPixelDepth(depth) );
99     }
100    
101     bool
102     parse_screen_prefs(const char *mode_str)
103     {
104 nigel 1.13 if ( ! mode_str )
105     return false;
106    
107 nigel 1.1 if (sscanf(mode_str, "win/%hd/%hd/%hd",
108     &init_width, &init_height, &init_depth) == 3)
109     display_type = DISPLAY_WINDOW;
110     else if (sscanf(mode_str, "win/%hd/%hd", &init_width, &init_height) == 2)
111     display_type = DISPLAY_WINDOW;
112     else if (strcmp(mode_str, "full") == 0)
113     display_type = DISPLAY_SCREEN;
114     else if (sscanf(mode_str, "full/%hd/%hd/%hd",
115     &init_width, &init_height, &init_depth) == 3)
116     display_type = DISPLAY_SCREEN;
117     else if (sscanf(mode_str, "full/%hd/%hd", &init_width, &init_height) == 2)
118     display_type = DISPLAY_SCREEN;
119     else if (sscanf(mode_str, "opengl/%hd/%hd/%hd",
120     &init_width, &init_height, &init_depth) == 3)
121     display_type = DISPLAY_OPENGL;
122     else if (sscanf(mode_str, "opengl/%hd/%hd", &init_width, &init_height) == 2)
123     display_type = DISPLAY_OPENGL;
124     else return false;
125    
126     return true;
127     }
128    
129 nigel 1.5 // Supported video modes
130     static vector<video_mode> VideoModes;
131    
132 nigel 1.1
133     // Add mode to list of supported modes
134     static void
135     add_mode(const uint16 width, const uint16 height,
136     const uint32 resolution_id, const uint32 bytes_per_row,
137 nigel 1.5 const uint32 user_data,
138 nigel 1.1 const video_depth depth)
139     {
140 nigel 1.3 vector<video_mode>::const_iterator i,
141     end = VideoModes.end();
142    
143     for (i = VideoModes.begin(); i != end; ++i)
144     if ( i->x == width && i->y == height &&
145     i->bytes_per_row == bytes_per_row && i->depth == depth )
146     {
147     D(NSLog(@"Duplicate mode (%hdx%hdx%ld, ID %02x, new ID %02x)\n",
148     width, height, depth, i->resolution_id, resolution_id));
149     return;
150     }
151    
152 nigel 1.1 video_mode mode;
153 nigel 1.3
154 nigel 1.1 mode.x = width;
155     mode.y = height;
156     mode.resolution_id = resolution_id;
157     mode.bytes_per_row = bytes_per_row;
158 nigel 1.5 mode.user_data = user_data;
159 nigel 1.1 mode.depth = depth;
160    
161 nigel 1.3 D(bug("Added video mode: w=%d h=%d d=%d(%d bits)\n",
162 nigel 1.1 width, height, depth, bits_from_depth(depth) ));
163    
164     VideoModes.push_back(mode);
165     }
166    
167     // Add standard list of windowed modes for given color depth
168     static void add_standard_modes(const video_depth depth)
169     {
170 nigel 1.3 D(bug("add_standard_modes: depth=%d(%d bits)\n",
171 nigel 1.1 depth, bits_from_depth(depth) ));
172    
173 nigel 1.5 add_mode(512, 384, 0x80, TrivialBytesPerRow(512, depth), 0, depth);
174     add_mode(640, 480, 0x81, TrivialBytesPerRow(640, depth), 0, depth);
175     add_mode(800, 600, 0x82, TrivialBytesPerRow(800, depth), 0, depth);
176     add_mode(832, 624, 0x83, TrivialBytesPerRow(832, depth), 0, depth);
177     add_mode(1024, 768, 0x84, TrivialBytesPerRow(1024, depth), 0, depth);
178     add_mode(1152, 768, 0x85, TrivialBytesPerRow(1152, depth), 0, depth);
179     add_mode(1152, 870, 0x86, TrivialBytesPerRow(1152, depth), 0, depth);
180     add_mode(1280, 1024, 0x87, TrivialBytesPerRow(1280, depth), 0, depth);
181     add_mode(1600, 1200, 0x88, TrivialBytesPerRow(1600, depth), 0, depth);
182 nigel 1.1 }
183    
184     // Helper function to get a 32bit int from a dictionary
185     static int32 getCFint32 (CFDictionaryRef dict, CFStringRef key)
186     {
187 nigel 1.6 CFNumberRef ref = (CFNumberRef) CFDictionaryGetValue(dict, key);
188 nigel 1.1
189 nigel 1.2 if ( ref )
190     {
191     int32 val;
192    
193     if ( CFNumberGetValue(ref, kCFNumberSInt32Type, &val) )
194     return val;
195     else
196     NSLog(@"getCFint32() - Failed to get the value %@", key);
197     }
198     else
199     NSLog(@"getCFint32() - Failed to get a 32bit int for %@", key);
200 nigel 1.1
201 nigel 1.2 return 0;
202 nigel 1.1 }
203    
204 nigel 1.6 // Nasty hack. Under 10.1, CGDisplayAvailableModes() does not provide bytes per row,
205 nigel 1.5 // and the emulator doesn't like setting the bytes per row after the screen,
206     // so we use a lot of magic numbers here.
207     // This will probably fail on some video hardware.
208     // I have tested on my G4 PowerBook 400 and G3 PowerBook Series 292
209    
210     static int
211     CGBytesPerRow(const uint16 width, const video_depth depth)
212     {
213     if ( depth == VDEPTH_8BIT )
214     switch ( width )
215     {
216     case 640:
217     case 720: return 768;
218     case 800:
219     case 896: return 1024;
220     case 1152: return 1280;
221     }
222    
223     if ( width == 720 && depth == VDEPTH_16BIT) return 1536;
224     if ( width == 720 && depth == VDEPTH_32BIT) return 3072;
225     if ( width == 800 && depth == VDEPTH_16BIT) return 1792;
226     if ( width == 800 && depth == VDEPTH_32BIT) return 3328;
227    
228     return TrivialBytesPerRow(width, depth);
229     }
230    
231 nigel 1.1 static bool add_CGDirectDisplay_modes()
232     {
233     #define kMaxDisplays 8
234     CGDirectDisplayID displays[kMaxDisplays];
235     CGDisplayErr err;
236     CGDisplayCount n;
237     int32 oldRes = 0,
238     res_id = 0x80;
239    
240    
241     err = CGGetActiveDisplayList(kMaxDisplays, displays, &n);
242     if ( err != CGDisplayNoErr )
243 nigel 1.3 n = 1, displays[n] = kCGDirectMainDisplay;
244 nigel 1.1
245 nigel 1.4 if ( n > 1 )
246     singleDisplay = NO;
247    
248 nigel 1.1 for ( CGDisplayCount dc = 0; dc < n; ++dc )
249     {
250     CGDirectDisplayID d = displays[dc];
251     CFArrayRef m = CGDisplayAvailableModes(d);
252    
253 nigel 1.4 if ( ! m ) // Store the current display mode
254 nigel 1.1 add_mode(CGDisplayPixelsWide(d),
255     CGDisplayPixelsHigh(d),
256     res_id++, CGDisplayBytesPerRow(d),
257 nigel 1.5 (const uint32) d,
258 nigel 1.1 DepthModeForPixelDepth(CGDisplayBitsPerPixel(d)));
259     else
260     {
261     CFIndex nModes = CFArrayGetCount(m);
262    
263     for ( CFIndex mc = 0; mc < nModes; ++mc )
264     {
265 nigel 1.6 CFDictionaryRef modeSpec = (CFDictionaryRef)
266     CFArrayGetValueAtIndex(m, mc);
267 nigel 1.1
268     int32 bpp = getCFint32(modeSpec, kCGDisplayBitsPerPixel);
269     int32 height = getCFint32(modeSpec, kCGDisplayHeight);
270     int32 width = getCFint32(modeSpec, kCGDisplayWidth);
271 nigel 1.6 #ifdef MAC_OS_X_VERSION_10_2
272     int32 bytes = getCFint32(modeSpec, kCGDisplayBytesPerRow);
273     #else
274 nigel 1.7 int32 bytes = 0;
275 nigel 1.6 #endif
276 nigel 1.1 video_depth depth = DepthModeForPixelDepth(bpp);
277    
278     if ( ! bpp || ! height || ! width )
279     {
280     NSLog(@"Could not get details of mode %d, display %d",
281     mc, dc);
282     return false;
283     }
284 nigel 1.3 #if VERBOSE
285 nigel 1.1 else
286     NSLog(@"Display %ld, spec = %@", d, modeSpec);
287     #endif
288    
289 nigel 1.6 if ( ! bytes )
290     {
291     NSLog(@"Could not get bytes per row, guessing");
292     bytes = CGBytesPerRow(width, depth);
293     }
294    
295 nigel 1.1 if ( ! oldRes )
296     oldRes = width * height;
297     else
298     if ( oldRes != width * height )
299     {
300     oldRes = width * height;
301     ++res_id;
302     }
303 nigel 1.3
304 nigel 1.6 add_mode(width, height, res_id, bytes, (const uint32) d, depth);
305 nigel 1.1 }
306     }
307     }
308    
309     return true;
310     }
311    
312 nigel 1.6 #ifdef CG_USE_ALPHA
313     // memset() by long instead of byte
314    
315     static void memsetl (long *buffer, long pattern, size_t length)
316     {
317     long *buf = (long *) buffer,
318     *end = buf + length/4;
319    
320     while ( ++buf < end )
321     *buf = pattern;
322     }
323    
324     // Sets the alpha channel in a image to full on, except for the corners
325    
326     static void mask_buffer (void *buffer, size_t width, size_t size)
327     {
328     long *bufl = (long *) buffer;
329     char *bufc = (char *) buffer;
330    
331    
332     memsetl(bufl, 0xFF000000, size);
333    
334    
335     // Round upper-left corner
336     *bufl = 0, *bufc+4 = 0; // XXXXX
337     bufc += width, *bufc++ = 0, *bufc++ = 0, *bufc++ = 0; // XXX
338     bufc += width, *bufc++ = 0, *bufc = 0; // XX
339     bufc += width, *bufc = 0; // X
340     bufc += width, *bufc = 0; // X
341    
342    
343     NSLog(@"Masked buffer");
344     }
345     #endif
346 nigel 1.5
347     // monitor_desc subclass for Mac OS X displays
348    
349     class OSX_monitor : public monitor_desc
350     {
351     public:
352     OSX_monitor(const vector<video_mode> &available_modes,
353     video_depth default_depth,
354     uint32 default_id);
355    
356     virtual void set_palette(uint8 *pal, int num);
357     virtual void switch_to_current_mode(void);
358    
359     void set_mac_frame_buffer(const video_mode mode);
360    
361     void video_close(void);
362     bool video_open (const video_mode &mode);
363    
364    
365     private:
366     bool init_opengl(const video_mode &mode);
367     bool init_screen( video_mode &mode);
368     bool init_window(const video_mode &mode);
369    
370    
371     #ifdef CGIMAGEREF
372 nigel 1.8 CGColorSpaceRef colourSpace;
373     uint8 *colourTable;
374 nigel 1.5 CGImageRef imageRef;
375 nigel 1.8 CGDataProviderRef provider;
376     short x, y, bpp, depth, bpr;
377 nigel 1.5 #endif
378     #ifdef NSBITMAP
379     NSBitmapImageRep *bitmap;
380     #endif
381     void *the_buffer;
382    
383    
384     // These record changes we made in setting full screen mode,
385     // so that we can set the display back as it was again.
386     CGDirectDisplayID theDisplay;
387     CFDictionaryRef originalMode,
388     newMode;
389     };
390    
391    
392     OSX_monitor :: OSX_monitor (const vector<video_mode> &available_modes,
393     video_depth default_depth,
394     uint32 default_id)
395     : monitor_desc (available_modes, default_depth, default_id)
396     {
397     #ifdef CGIMAGEREF
398 nigel 1.8 colourSpace = nil;
399     colourTable = (uint8 *) malloc(256 * 3);
400 nigel 1.5 imageRef = nil;
401 nigel 1.8 provider = nil;
402 nigel 1.5 #endif
403     #ifdef NSBITMAP
404     bitmap = nil;
405     #endif
406     newMode = originalMode = nil;
407     the_buffer = NULL;
408     theDisplay = nil;
409     };
410    
411 nigel 1.8 // Should also have a destructor which does
412     //#ifdef CGIMAGEREF
413     // free(colourTable);
414     //#endif
415    
416 nigel 1.5
417 nigel 1.1 // Set Mac frame layout and base address (uses the_buffer/MacFrameBaseMac)
418 nigel 1.5 void
419     OSX_monitor::set_mac_frame_buffer(const video_mode mode)
420 nigel 1.1 {
421     #if !REAL_ADDRESSING && !DIRECT_ADDRESSING
422 nigel 1.5 switch ( mode.depth )
423 nigel 1.1 {
424     // case VDEPTH_15BIT:
425     case VDEPTH_16BIT: MacFrameLayout = FLAYOUT_HOST_555; break;
426     // case VDEPTH_24BIT:
427     case VDEPTH_32BIT: MacFrameLayout = FLAYOUT_HOST_888; break;
428     default : MacFrameLayout = FLAYOUT_DIRECT;
429     }
430 nigel 1.5 set_mac_frame_base(MacFrameBaseMac);
431 nigel 1.1
432     // Set variables used by UAE memory banking
433 nigel 1.6 MacFrameBaseHost = (uint8 *) the_buffer;
434 nigel 1.5 MacFrameSize = mode.bytes_per_row * mode.y;
435 nigel 1.1 InitFrameBufferMapping();
436     #else
437 nigel 1.6 set_mac_frame_base((unsigned int)Host2MacAddr((uint8 *)the_buffer));
438 nigel 1.1 #endif
439 nigel 1.5 D(bug("mac_frame_base = %08x\n", get_mac_frame_base()));
440 nigel 1.1 }
441    
442 nigel 1.4 static void
443     resizeWinBy(const short deltaX, const short deltaY)
444 nigel 1.1 {
445     NSRect rect = [the_win frame];
446    
447     D(bug("resizeWinBy(%d,%d) - ", deltaX, deltaY));
448     D(bug("old x=%g, y=%g", rect.size.width, rect.size.height));
449    
450     rect.size.width += deltaX;
451     rect.size.height += deltaY;
452    
453     D(bug(", new x=%g, y=%g\n", rect.size.width, rect.size.height));
454    
455 nigel 1.10 [the_win setFrame: rect display: YES animate: YES];
456     [the_win center];
457 nigel 1.1 rect = [the_win frame];
458     }
459    
460     void resizeWinTo(const uint16 newWidth, const uint16 newHeight)
461     {
462     int deltaX = newWidth - [output width],
463     deltaY = newHeight - [output height];
464    
465     D(bug("resizeWinTo(%d,%d)\n", newWidth, newHeight));
466    
467     if ( deltaX || deltaY )
468     resizeWinBy(deltaX, deltaY);
469     }
470    
471     // Open window
472 nigel 1.5 bool
473     OSX_monitor::init_window(const video_mode &mode)
474 nigel 1.1 {
475 nigel 1.3 D(bug("init_window: depth=%d(%d bits)\n",
476 nigel 1.1 mode.depth, bits_from_depth(mode.depth) ));
477    
478    
479     // Set absolute mouse mode
480     ADBSetRelMouseMode(false);
481    
482    
483 nigel 1.8 // Is the window open?
484 nigel 1.4 if ( ! the_win )
485 nigel 1.1 {
486     ErrorAlert(STR_OPEN_WINDOW_ERR);
487     return false;
488     }
489     resizeWinTo(mode.x, mode.y);
490    
491    
492     // Create frame buffer ("height + 2" for safety)
493 nigel 1.8 int the_buffer_size = mode.bytes_per_row * (mode.y + 2);
494    
495 nigel 1.1 the_buffer = calloc(the_buffer_size, 1);
496 nigel 1.4 if ( ! the_buffer )
497 nigel 1.1 {
498     NSLog(@"calloc(%d) failed", the_buffer_size);
499     ErrorAlert(STR_NO_MEM_ERR);
500     return false;
501     }
502     D(bug("the_buffer = %p\n", the_buffer));
503    
504    
505 nigel 1.8 unsigned char *offsetBuffer = (unsigned char *) the_buffer;
506     offsetBuffer += 1; // OS X NSBitmaps are RGBA, but Basilisk generates ARGB
507 nigel 1.1
508     #ifdef CGIMAGEREF
509     switch ( mode.depth )
510     {
511 nigel 1.8 case VDEPTH_1BIT: bpp = 1; break;
512     case VDEPTH_2BIT: bpp = 2; break;
513     case VDEPTH_4BIT: bpp = 4; break;
514     case VDEPTH_8BIT: bpp = 8; break;
515     case VDEPTH_16BIT: bpp = 5; break;
516     case VDEPTH_32BIT: bpp = 8; break;
517     }
518    
519     x = mode.x, y = mode.y, depth = bits_from_depth(mode.depth), bpr = mode.bytes_per_row;
520    
521     colourSpace = CGColorSpaceCreateDeviceRGB();
522    
523     if ( mode.depth < VDEPTH_16BIT )
524     {
525     CGColorSpaceRef oldColourSpace = colourSpace;
526    
527     colourSpace = CGColorSpaceCreateIndexed(colourSpace, 255, colourTable);
528    
529     CGColorSpaceRelease(oldColourSpace);
530 nigel 1.1 }
531    
532     if ( ! colourSpace )
533     {
534     ErrorAlert("No valid colour space");
535     return false;
536     }
537    
538     provider = CGDataProviderCreateWithData(NULL, the_buffer,
539     the_buffer_size, NULL);
540     if ( ! provider )
541     {
542     ErrorAlert("Could not create CGDataProvider from buffer data");
543     return false;
544     }
545 nigel 1.8
546     imageRef = CGImageCreate(x, y, bpp, depth, bpr, colourSpace,
547 nigel 1.6 #ifdef CG_USE_ALPHA
548     kCGImageAlphaPremultipliedFirst,
549     #else
550 nigel 1.1 kCGImageAlphaNoneSkipFirst,
551 nigel 1.6 #endif
552 nigel 1.1 provider,
553 nigel 1.6 NULL, // colourMap translation table
554     NO, // shouldInterpolate colours?
555 nigel 1.1 kCGRenderingIntentDefault);
556     if ( ! imageRef )
557     {
558     ErrorAlert("Could not create CGImage from CGDataProvider");
559     return false;
560     }
561    
562     [output readyToDraw: imageRef
563 nigel 1.8 bitmap: offsetBuffer
564     imageWidth: x
565     imageHeight: y];
566    
567 nigel 1.6
568     #ifdef CG_USE_ALPHA
569 nigel 1.8 mask_buffer(the_buffer, x, the_buffer_size);
570 nigel 1.9
571     /* Create an image mask with this call? */
572     //CG_EXTERN CGImageRef
573     //CGImageMaskCreate(size_t width, size_t height, size_t bitsPerComponent,
574     // size_t bitsPerPixel, size_t bytesPerRow,
575     // CGDataProviderRef provider, const float decode[], bool shouldInterpolate);
576 nigel 1.6 #endif
577 nigel 1.8
578     return true;
579     #endif
580    
581    
582     #ifndef CGIMAGEREF
583     short bitsPer, samplesPer; // How big is each Pixel?
584    
585     if ( mode.depth == VDEPTH_1BIT )
586     bitsPer = 1;
587     else
588     bitsPer = 8;
589    
590     if ( mode.depth == VDEPTH_32BIT )
591     samplesPer = 3;
592     else
593     samplesPer = 1;
594 nigel 1.1 #endif
595    
596 nigel 1.8
597 nigel 1.1 #ifdef NSBITMAP
598     bitmap = [NSBitmapImageRep alloc];
599     bitmap = [bitmap initWithBitmapDataPlanes: (unsigned char **) &offsetBuffer
600     pixelsWide: mode.x
601     pixelsHigh: mode.y
602     bitsPerSample: bitsPer
603     samplesPerPixel: samplesPer
604     hasAlpha: NO
605     isPlanar: NO
606     colorSpaceName: NSCalibratedRGBColorSpace
607     bytesPerRow: mode.bytes_per_row
608     bitsPerPixel: bits_from_depth(mode.depth)];
609    
610 nigel 1.4 if ( ! bitmap )
611 nigel 1.1 {
612     ErrorAlert("Could not allocate an NSBitmapImageRep");
613     return false;
614     }
615    
616     [output readyToDraw: bitmap
617     imageWidth: mode.x
618     imageHeight: mode.y];
619     #endif
620    
621     #ifdef CGDRAWBITMAP
622     [output readyToDraw: offsetBuffer
623     width: mode.x
624     height: mode.y
625     bps: bitsPer
626     spp: samplesPer
627     bpp: bits_from_depth(mode.depth)
628     bpr: mode.bytes_per_row
629     isPlanar: NO
630     hasAlpha: NO];
631     #endif
632    
633     return true;
634     }
635    
636     #import <AppKit/NSEvent.h>
637 nigel 1.3 #import <Carbon/Carbon.h>
638 nigel 1.5 #import "NNThread.h"
639 nigel 1.1
640 nigel 1.5 bool
641     OSX_monitor::init_screen(video_mode &mode)
642 nigel 1.1 {
643     // Set absolute mouse mode
644     ADBSetRelMouseMode(false);
645    
646 nigel 1.5 // Display stored by add_CGDirectDisplay_modes()
647     theDisplay = (CGDirectDisplayID) mode.user_data;
648 nigel 1.1
649     originalMode = CGDisplayCurrentMode(theDisplay);
650 nigel 1.4 if ( ! originalMode )
651 nigel 1.3 {
652     ErrorSheet(@"Could not get current mode of display", the_win);
653     return false;
654     }
655 nigel 1.1
656 nigel 1.3 D(NSLog(@"About to call CGDisplayBestModeForParameters()"));
657 nigel 1.1 newMode = CGDisplayBestModeForParameters(theDisplay,
658     bits_from_depth(mode.depth),
659     mode.x, mode.y, NULL);
660 nigel 1.4 if ( ! newMode )
661 nigel 1.1 {
662 nigel 1.3 ErrorSheet(@"Could not find a matching screen mode", the_win);
663 nigel 1.1 return false;
664     }
665    
666 nigel 1.3 // This sometimes takes ages to return after the window is genied,
667     // so for now we leave it onscreen
668     // [the_win miniaturize: nil];
669 nigel 1.1
670 nigel 1.3 D(NSLog(@"About to call CGDisplayCapture()"));
671     if ( CGDisplayCapture(theDisplay) != CGDisplayNoErr )
672     {
673     // [the_win deminiaturize: nil];
674     ErrorSheet(@"Could not capture display", the_win);
675     return false;
676     }
677 nigel 1.1
678 nigel 1.3 D(NSLog(@"About to call CGDisplaySwitchToMode()"));
679 nigel 1.1 if ( CGDisplaySwitchToMode(theDisplay, newMode) != CGDisplayNoErr )
680     {
681 nigel 1.4 CGDisplayRelease(theDisplay);
682 nigel 1.3 // [the_win deminiaturize: nil];
683     ErrorSheet(@"Could not switch to matching screen mode", the_win);
684 nigel 1.1 return false;
685     }
686    
687 nigel 1.5 the_buffer = CGDisplayBaseAddress(theDisplay);
688     if ( ! the_buffer )
689     {
690     CGDisplaySwitchToMode(theDisplay, originalMode);
691     CGDisplayRelease(theDisplay);
692     // [the_win deminiaturize: nil];
693     ErrorSheet(@"Could not get base address of screen", the_win);
694     return false;
695     }
696 nigel 1.6
697 nigel 1.3 if ( mode.bytes_per_row != CGDisplayBytesPerRow(theDisplay) )
698     {
699     D(bug("Bytes per row (%d) doesn't match current (%ld)\n",
700     mode.bytes_per_row, CGDisplayBytesPerRow(theDisplay)));
701     mode.bytes_per_row = CGDisplayBytesPerRow(theDisplay);
702     }
703    
704     HideMenuBar();
705 nigel 1.4
706     if ( singleDisplay )
707     {
708     CGDisplayHideCursor(theDisplay);
709    
710 nigel 1.7 [output startedFullScreen: theDisplay];
711 nigel 1.5
712     // Send emulated mouse to current location
713 nigel 1.7 [output fullscreenMouseMove];
714 nigel 1.4 }
715     else
716     {
717     // Should set up something to hide the cursor when it enters theDisplay?
718     }
719 nigel 1.1
720     return true;
721     }
722    
723 nigel 1.5
724     bool
725     OSX_monitor::init_opengl(const video_mode &mode)
726 nigel 1.1 {
727     ErrorAlert("Sorry. OpenGL mode is not implemented yet");
728     return false;
729     }
730    
731     /*
732     * Initialization
733     */
734 nigel 1.5 static bool
735     monitor_init(const video_mode &init_mode)
736     {
737     OSX_monitor *monitor;
738     BOOL success;
739    
740     monitor = new OSX_monitor(VideoModes, init_mode.depth,
741     init_mode.resolution_id);
742     success = monitor->video_open(init_mode);
743    
744     if ( success )
745     {
746     monitor->set_mac_frame_buffer(init_mode);
747     VideoMonitors.push_back(monitor);
748     return YES;
749     }
750    
751     return NO;
752     }
753 nigel 1.1
754     bool VideoInit(bool classic)
755     {
756     // Read frame skip prefs
757     frame_skip = PrefsFindInt32("frameskip");
758     if (frame_skip == 0)
759     frame_skip = 1;
760    
761     // Get screen mode from preferences
762     const char *mode_str;
763     if (classic)
764     mode_str = "win/512/342";
765     else
766     mode_str = PrefsFindString("screen");
767    
768     // Determine display_type and init_width, height & depth
769     parse_screen_prefs(mode_str);
770    
771     // Construct list of supported modes
772     if (classic)
773 nigel 1.5 add_mode(512, 342, 0x80, 64, 0, VDEPTH_1BIT);
774 nigel 1.1 else
775     switch ( display_type )
776     {
777     case DISPLAY_SCREEN:
778     if ( ! add_CGDirectDisplay_modes() )
779     {
780     ErrorAlert("Unable to get list of displays for full screen mode");
781     return false;
782     }
783     break;
784     case DISPLAY_OPENGL:
785     // Same as window depths and sizes?
786     case DISPLAY_WINDOW:
787 nigel 1.8 #ifdef CGIMAGEREF
788 nigel 1.7 add_standard_modes(VDEPTH_1BIT);
789 nigel 1.8 add_standard_modes(VDEPTH_2BIT);
790     add_standard_modes(VDEPTH_4BIT);
791 nigel 1.7 add_standard_modes(VDEPTH_8BIT);
792     add_standard_modes(VDEPTH_16BIT);
793     #endif
794 nigel 1.1 add_standard_modes(VDEPTH_32BIT);
795     break;
796     }
797    
798 nigel 1.5 // video_init_depth_list(); Now done in monitor_desc constructor?
799 nigel 1.1
800     #if DEBUG
801     bug("Available video modes:\n");
802     vector<video_mode>::const_iterator i, end = VideoModes.end();
803     for (i = VideoModes.begin(); i != end; ++i)
804     bug(" %dx%d (ID %02x), %s\n", i->x, i->y, i->resolution_id,
805     colours_from_depth(i->depth));
806     #endif
807    
808 nigel 1.3 D(bug("VideoInit: width=%hd height=%hd depth=%d\n",
809 nigel 1.1 init_width, init_height, init_depth));
810    
811     // Find requested default mode and open display
812     if (VideoModes.size() > 0)
813     {
814     // Find mode with specified dimensions
815     std::vector<video_mode>::const_iterator i, end = VideoModes.end();
816     for (i = VideoModes.begin(); i != end; ++i)
817     {
818 nigel 1.3 D(bug("VideoInit: w=%d h=%d d=%d\n",
819 nigel 1.1 i->x, i->y, bits_from_depth(i->depth)));
820     if (i->x == init_width && i->y == init_height
821     && bits_from_depth(i->depth) == init_depth)
822 nigel 1.5 return monitor_init(*i);
823 nigel 1.1 }
824     }
825    
826     char str[150];
827     sprintf(str, "Cannot open selected video mode\r(%hd x %hd, %s).\r%s",
828     init_width, init_height,
829     colours_from_depth(init_depth), "Using lowest resolution");
830     WarningAlert(str);
831    
832 nigel 1.5 return monitor_init(VideoModes[0]);
833 nigel 1.1 }
834    
835    
836     // Open display for specified mode
837 nigel 1.5 bool
838     OSX_monitor::video_open(const video_mode &mode)
839 nigel 1.1 {
840 nigel 1.3 D(bug("video_open: width=%d height=%d depth=%d bytes_per_row=%d\n",
841 nigel 1.1 mode.x, mode.y, bits_from_depth(mode.depth), mode.bytes_per_row));
842    
843     // Open display
844 nigel 1.3 switch ( display_type )
845     {
846     case DISPLAY_WINDOW: return init_window(mode);
847     case DISPLAY_SCREEN: return init_screen((video_mode &)mode);
848     case DISPLAY_OPENGL: return init_opengl(mode);
849 nigel 1.1 }
850    
851 nigel 1.3 return false;
852 nigel 1.1 }
853    
854    
855 nigel 1.5 void
856     OSX_monitor::video_close()
857 nigel 1.1 {
858     D(bug("video_close()\n"));
859    
860     switch ( display_type ) {
861     case DISPLAY_WINDOW:
862     // Stop redraw thread
863     [output disableDrawing];
864    
865     // Free frame buffer stuff
866     #ifdef CGIMAGEREF
867     CGImageRelease(imageRef);
868 nigel 1.8 CGColorSpaceRelease(colourSpace);
869     CGDataProviderRelease(provider);
870 nigel 1.1 #endif
871     #ifdef NSBITMAP
872     [bitmap release];
873     #endif
874     free(the_buffer);
875    
876     break;
877    
878     case DISPLAY_SCREEN:
879     if ( theDisplay && originalMode )
880     {
881 nigel 1.4 if ( singleDisplay )
882     CGDisplayShowCursor(theDisplay);
883 nigel 1.3 ShowMenuBar();
884 nigel 1.1 CGDisplaySwitchToMode(theDisplay, originalMode);
885     CGDisplayRelease(theDisplay);
886 nigel 1.3 //[the_win deminiaturize: nil];
887 nigel 1.1 }
888     break;
889    
890     case DISPLAY_OPENGL:
891     break;
892     }
893     }
894    
895    
896     /*
897     * Deinitialization
898     */
899    
900     void VideoExit(void)
901     {
902 nigel 1.5 // Close displays
903     vector<monitor_desc *>::iterator i, end;
904    
905     end = VideoMonitors.end();
906    
907     for (i = VideoMonitors.begin(); i != end; ++i)
908     dynamic_cast<OSX_monitor *>(*i)->video_close();
909 nigel 1.10
910     VideoMonitors.clear();
911     VideoModes.clear();
912 nigel 1.1 }
913    
914    
915     /*
916     * Set palette
917     */
918    
919 nigel 1.5 void
920     OSX_monitor::set_palette(uint8 *pal, int num)
921 nigel 1.1 {
922 nigel 1.3 if ( [output isFullScreen] && CGDisplayCanSetPalette(theDisplay)
923 nigel 1.5 && ! IsDirectMode(get_current_mode()) )
924 nigel 1.1 {
925     CGDirectPaletteRef CGpal;
926     CGDisplayErr err;
927    
928    
929     CGpal = CGPaletteCreateWithByteSamples((CGDeviceByteColor *)pal, num);
930     err = CGDisplaySetPalette(theDisplay, CGpal);
931     if ( err != noErr )
932     NSLog(@"Failed to set palette, error = %d", err);
933     CGPaletteRelease(CGpal);
934     }
935 nigel 1.8
936     #ifdef CGIMAGEREF
937     if ( display_type != DISPLAY_WINDOW )
938     return;
939    
940     // To change the palette, we have to regenerate
941     // the CGImageRef with the new colour space.
942    
943     CGImageRef oldImageRef = imageRef;
944     CGColorSpaceRef oldColourSpace = colourSpace;
945    
946     colourSpace = CGColorSpaceCreateDeviceRGB();
947    
948     if ( depth < 16 )
949     {
950     CGColorSpaceRef tempColourSpace = colourSpace;
951    
952     colourSpace = CGColorSpaceCreateIndexed(colourSpace, 255, pal);
953     CGColorSpaceRelease(tempColourSpace);
954     }
955    
956     if ( ! colourSpace )
957     {
958     ErrorAlert("No valid colour space");
959     return;
960     }
961    
962     imageRef = CGImageCreate(x, y, bpp, depth, bpr, colourSpace,
963     #ifdef CG_USE_ALPHA
964     kCGImageAlphaPremultipliedFirst,
965     #else
966     kCGImageAlphaNoneSkipFirst,
967     #endif
968     provider,
969     NULL, // colourMap translation table
970     NO, // shouldInterpolate colours?
971     kCGRenderingIntentDefault);
972     if ( ! imageRef )
973     {
974     ErrorAlert("Could not create CGImage from CGDataProvider");
975     return;
976     }
977    
978     unsigned char *offsetBuffer = (unsigned char *) the_buffer;
979     offsetBuffer += 1; // OS X NSBitmaps are RGBA, but Basilisk generates ARGB
980    
981     [output readyToDraw: imageRef
982     bitmap: offsetBuffer
983     imageWidth: x
984     imageHeight: y];
985    
986     CGColorSpaceRelease(oldColourSpace);
987     CGImageRelease(oldImageRef);
988     #endif
989 nigel 1.1 }
990    
991    
992     /*
993     * Switch video mode
994     */
995    
996 nigel 1.5 void
997     OSX_monitor::switch_to_current_mode(void)
998 nigel 1.1 {
999 nigel 1.5 video_mode mode = get_current_mode();
1000 nigel 1.4 char *failure = NULL;
1001    
1002    
1003     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));
1004 nigel 1.5
1005 nigel 1.4 if ( display_type == DISPLAY_SCREEN && originalMode )
1006     {
1007     D(NSLog(@"About to call CGDisplayBestModeForParameters()"));
1008     newMode = CGDisplayBestModeForParameters(theDisplay,
1009     bits_from_depth(mode.depth),
1010     mode.x, mode.y, NULL);
1011     if ( ! newMode )
1012     failure = "Could not find a matching screen mode";
1013     else
1014     {
1015     D(NSLog(@"About to call CGDisplaySwitchToMode()"));
1016     if ( CGDisplaySwitchToMode(theDisplay, newMode) != CGDisplayNoErr )
1017     failure = "Could not switch to matching screen mode";
1018     }
1019    
1020 nigel 1.5 // For mouse event processing: update screen height
1021     [output startedFullScreen: theDisplay];
1022    
1023 nigel 1.4 if ( ! failure &&
1024     mode.bytes_per_row != CGDisplayBytesPerRow(theDisplay) )
1025     {
1026     D(bug("Bytes per row (%d) doesn't match current (%ld)\n",
1027     mode.bytes_per_row, CGDisplayBytesPerRow(theDisplay)));
1028 nigel 1.5 mode.bytes_per_row = CGDisplayBytesPerRow(theDisplay);
1029 nigel 1.4 }
1030    
1031     if ( ! failure &&
1032     ! ( the_buffer = CGDisplayBaseAddress(theDisplay) ) )
1033     failure = "Could not get base address of screen";
1034 nigel 1.7
1035 nigel 1.9 }
1036 nigel 1.12 #ifdef CGIMAGEREF
1037 nigel 1.9 // Clean up the old CGImageRef stuff
1038     else if ( display_type == DISPLAY_WINDOW && imageRef )
1039     {
1040     CGImageRef oldImageRef = imageRef;
1041     CGColorSpaceRef oldColourSpace = colourSpace;
1042     CGDataProviderRef oldProvider = provider;
1043     void *oldBuffer = the_buffer;
1044    
1045     if ( video_open(mode) )
1046     {
1047     CGImageRelease(oldImageRef);
1048     CGColorSpaceRelease(oldColourSpace);
1049     CGDataProviderRelease(oldProvider);
1050     free(oldBuffer);
1051     }
1052     else
1053     failure = "Could not video_open() requested mode";
1054 nigel 1.4 }
1055 nigel 1.12 #endif
1056 nigel 1.4 else if ( ! video_open(mode) )
1057     failure = "Could not video_open() requested mode";
1058 nigel 1.7
1059     if ( ! failure && display_type == DISPLAY_SCREEN )
1060     {
1061     // Whenever we change screen resolution, the MacOS mouse starts
1062     // up in the top left corner. Send real mouse to that location
1063     // if ( CGDisplayMoveCursorToPoint(theDisplay, CGPointMake(15,15))
1064     // == CGDisplayNoErr )
1065     // {
1066     //
1067     [output fullscreenMouseMove];
1068     // }
1069     // else
1070     // failure = "Could move (jump) cursor on screen";
1071     }
1072 nigel 1.4
1073     if ( failure )
1074 nigel 1.1 {
1075 nigel 1.4 NSLog(@"In switch_to_current_mode():");
1076     NSLog(@"%s.", failure);
1077     video_close();
1078 nigel 1.1 if ( display_type == DISPLAY_SCREEN )
1079     ErrorAlert("Cannot switch screen to selected video mode");
1080     else
1081     ErrorAlert(STR_OPEN_WINDOW_ERR);
1082     QuitEmulator();
1083     }
1084 nigel 1.4 else
1085 nigel 1.5 set_mac_frame_buffer(mode);
1086 nigel 1.1 }
1087    
1088     /*
1089     * Close down full-screen mode
1090     * (if bringing up error alerts is unsafe while in full-screen mode)
1091     */
1092    
1093     void VideoQuitFullScreen(void)
1094     {
1095     }
1096    
1097    
1098     /*
1099     * Mac VBL interrupt
1100     */
1101    
1102     void VideoInterrupt(void)
1103     {
1104     }
1105    
1106    
1107     // This function is called on non-threaded platforms from a timer interrupt
1108     void VideoRefresh(void)
1109     {
1110 nigel 1.12 }
1111    
1112    
1113    
1114     // Deal with a memory access signal referring to the screen.
1115     // For now, just ignore
1116     bool Screen_fault_handler(char *a, char *b)
1117     {
1118     // NSLog(@"Got a screen fault %lx %lx", a, b);
1119     // [output setNeedsDisplay: YES];
1120     return YES;
1121 nigel 1.1 }