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

# Content
1 /*
2 * $Id: video_macosx.mm,v 1.4 2002/06/05 10:11:40 nigel Exp $
3 *
4 * video_macosx.mm - Interface between Basilisk II and Cocoa windowing.
5 * Based on video_amiga.cpp and video_x.cpp
6 *
7 * Basilisk II (C) 1997-2002 Christian Bauer
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24
25 #include "sysdeps.h"
26
27 #ifdef HAVE_PTHREADS
28 # include <pthread.h>
29 #endif
30
31 #include <adb.h>
32 #include <cpu_emulation.h>
33 #include <main.h>
34 #include "macos_util_macosx.h"
35 #include <prefs.h>
36 #include <user_strings.h>
37 #include "video_macosx.h"
38
39 #define DEBUG 0
40 #include "debug.h"
41
42 #ifdef NSBITMAP
43 #import <AppKit/NSBitmapImageRep.h>
44 #endif
45
46 #import <Foundation/NSString.h> // Needed for NSLog(@"")
47 #import "misc_macosx.h" // WarningSheet() prototype
48
49
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 init_depth = 32;
57
58 EmulatorView *output = nil; // Set by [EmulatorView init]
59 NSWindow *the_win = nil; // Set by [Emulator awakeFromNib]
60
61 static BOOL singleDisplay = YES;
62
63 /*
64 * Utility functions
65 */
66
67 static uint8
68 bits_from_depth(const video_depth depth)
69 {
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 static char *
79 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 static char *
95 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 // Supported video modes
126 static vector<video_mode> VideoModes;
127
128
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 const uint32 user_data,
134 const video_depth depth)
135 {
136 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 video_mode mode;
149
150 mode.x = width;
151 mode.y = height;
152 mode.resolution_id = resolution_id;
153 mode.bytes_per_row = bytes_per_row;
154 mode.user_data = user_data;
155 mode.depth = depth;
156
157 D(bug("Added video mode: w=%d h=%d d=%d(%d bits)\n",
158 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 D(bug("add_standard_modes: depth=%d(%d bits)\n",
167 depth, bits_from_depth(depth) ));
168
169 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 }
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 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
197 return 0;
198 }
199
200 // 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 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 n = 1, displays[n] = kCGDirectMainDisplay;
240
241 if ( n > 1 )
242 singleDisplay = NO;
243
244 for ( CGDisplayCount dc = 0; dc < n; ++dc )
245 {
246 CGDirectDisplayID d = displays[dc];
247 CFArrayRef m = CGDisplayAvailableModes(d);
248
249 if ( ! m ) // Store the current display mode
250 add_mode(CGDisplayPixelsWide(d),
251 CGDisplayPixelsHigh(d),
252 res_id++, CGDisplayBytesPerRow(d),
253 (const uint32) d,
254 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 #if VERBOSE
275 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
288 add_mode(width, height, res_id,
289 CGBytesPerRow(width, depth), (const uint32) d, depth);
290 }
291 }
292 }
293
294 return true;
295 }
296
297
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 // Set Mac frame layout and base address (uses the_buffer/MacFrameBaseMac)
357 void
358 OSX_monitor::set_mac_frame_buffer(const video_mode mode)
359 {
360 #if !REAL_ADDRESSING && !DIRECT_ADDRESSING
361 switch ( mode.depth )
362 {
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 set_mac_frame_base(MacFrameBaseMac);
370
371 // Set variables used by UAE memory banking
372 MacFrameBaseHost = the_buffer;
373 MacFrameSize = mode.bytes_per_row * mode.y;
374 InitFrameBufferMapping();
375 #else
376 set_mac_frame_base(Host2MacAddr(the_buffer));
377 #endif
378 D(bug("mac_frame_base = %08x\n", get_mac_frame_base()));
379 }
380
381 static void
382 resizeWinBy(const short deltaX, const short deltaY)
383 {
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 bool
411 OSX_monitor::init_window(const video_mode &mode)
412 {
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 D(bug("init_window: depth=%d(%d bits)\n",
421 mode.depth, bits_from_depth(mode.depth) ));
422
423
424 // Set absolute mouse mode
425 ADBSetRelMouseMode(false);
426
427
428 // Open window
429 if ( ! the_win )
430 {
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 if ( ! the_buffer )
441 {
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 if ( ! bitmap )
522 {
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 #import <Carbon/Carbon.h>
549 #import "NNThread.h"
550
551 bool
552 OSX_monitor::init_screen(video_mode &mode)
553 {
554 // Set absolute mouse mode
555 ADBSetRelMouseMode(false);
556
557 // Display stored by add_CGDirectDisplay_modes()
558 theDisplay = (CGDirectDisplayID) mode.user_data;
559
560 originalMode = CGDisplayCurrentMode(theDisplay);
561 if ( ! originalMode )
562 {
563 ErrorSheet(@"Could not get current mode of display", the_win);
564 return false;
565 }
566
567 D(NSLog(@"About to call CGDisplayBestModeForParameters()"));
568 newMode = CGDisplayBestModeForParameters(theDisplay,
569 bits_from_depth(mode.depth),
570 mode.x, mode.y, NULL);
571 if ( ! newMode )
572 {
573 ErrorSheet(@"Could not find a matching screen mode", the_win);
574 return false;
575 }
576
577 // 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
581 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
589 D(NSLog(@"About to call CGDisplaySwitchToMode()"));
590 if ( CGDisplaySwitchToMode(theDisplay, newMode) != CGDisplayNoErr )
591 {
592 CGDisplayRelease(theDisplay);
593 // [the_win deminiaturize: nil];
594 ErrorSheet(@"Could not switch to matching screen mode", the_win);
595 return false;
596 }
597
598 // 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 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
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
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 }
646 else
647 {
648 // Should set up something to hide the cursor when it enters theDisplay?
649 }
650
651 return true;
652 }
653
654
655 bool
656 OSX_monitor::init_opengl(const video_mode &mode)
657 {
658 ErrorAlert("Sorry. OpenGL mode is not implemented yet");
659 return false;
660 }
661
662 /*
663 * Initialization
664 */
665 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
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 add_mode(512, 342, 0x80, 64, 0, VDEPTH_1BIT);
705 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 // video_init_depth_list(); Now done in monitor_desc constructor?
726
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 D(bug("VideoInit: width=%hd height=%hd depth=%d\n",
736 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 D(bug("VideoInit: w=%d h=%d d=%d\n",
746 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 return monitor_init(*i);
750 }
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 return monitor_init(VideoModes[0]);
760 }
761
762
763 // Open display for specified mode
764 bool
765 OSX_monitor::video_open(const video_mode &mode)
766 {
767 D(bug("video_open: width=%d height=%d depth=%d bytes_per_row=%d\n",
768 mode.x, mode.y, bits_from_depth(mode.depth), mode.bytes_per_row));
769
770 // Open display
771 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 }
777
778 return false;
779 }
780
781
782 void
783 OSX_monitor::video_close()
784 {
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 if ( singleDisplay )
807 CGDisplayShowCursor(theDisplay);
808 ShowMenuBar();
809 CGDisplaySwitchToMode(theDisplay, originalMode);
810 CGDisplayRelease(theDisplay);
811 //[the_win deminiaturize: nil];
812 }
813 break;
814
815 case DISPLAY_OPENGL:
816 break;
817 }
818 }
819
820
821 /*
822 * Deinitialization
823 */
824
825 void VideoExit(void)
826 {
827 // 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 }
835
836
837 /*
838 * Set palette
839 */
840
841 void
842 OSX_monitor::set_palette(uint8 *pal, int num)
843 {
844 if ( [output isFullScreen] && CGDisplayCanSetPalette(theDisplay)
845 && ! IsDirectMode(get_current_mode()) )
846 {
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 void
865 OSX_monitor::switch_to_current_mode(void)
866 {
867 video_mode mode = get_current_mode();
868 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
873 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 // For mouse event processing: update screen height
889 [output startedFullScreen: theDisplay];
890
891 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 mode.bytes_per_row = CGDisplayBytesPerRow(theDisplay);
897 }
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 {
911 NSLog(@"In switch_to_current_mode():");
912 NSLog(@"%s.", failure);
913 video_close();
914 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 else
921 set_mac_frame_buffer(mode);
922 }
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 }