ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/video_macosx.mm
Revision: 1.5
Committed: 2002-07-02T09:47:57Z (22 years, 1 month ago) by nigel
Branch: MAIN
Changes since 1.4: +198 -80 lines
Log Message:
Compatibility with latest C++ monitor object. Unfortunately this requires
a terrible hack to guess the number of bytes per row for each mode, which
is terribly hardware dependant, but is the only way at the moment.

File Contents

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