ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/EmulatorView.mm
Revision: 1.15
Committed: 2006-05-14T10:17:06Z (18 years, 6 months ago) by nigel
Branch: MAIN
CVS Tags: nigel-build-19
Changes since 1.14: +2 -2 lines
Log Message:
Some windowed graphics drawing methods cause the snapshot code to fail
if you have changed the depth since boot (seems to be something strange
with the parameters that I still haven't worked out). If this happens,
we now put a suggested workaround in the warning message.

File Contents

# User Rev Content
1 nigel 1.1 /*
2 nigel 1.9 * EmulatorView.mm - Custom NSView for Basilisk II windowed graphics output
3 nigel 1.1 *
4 nigel 1.15 * $Id: EmulatorView.mm,v 1.14 2005/01/30 21:42:13 gbeauche Exp $
5 nigel 1.1 *
6 gbeauche 1.14 * Basilisk II (C) 1997-2005 Christian Bauer
7 nigel 1.1 *
8     * This program is free software; you can redistribute it and/or modify
9     * it under the terms of the GNU General Public License as published by
10     * the Free Software Foundation; either version 2 of the License, or
11     * (at your option) any later version.
12     *
13     * This program is distributed in the hope that it will be useful,
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16     * GNU General Public License for more details.
17     *
18     * You should have received a copy of the GNU General Public License
19     * along with this program; if not, write to the Free Software
20     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21     */
22    
23     #import "sysdeps.h" // Types used in Basilisk C++ code,
24    
25     #define DEBUG 0
26     #import <debug.h>
27    
28     #import <Cocoa/Cocoa.h>
29    
30     #import "main_macosx.h" // For WarningAlert() et al prototypes
31     #import "misc_macosx.h" // For InfoSheet() prototype
32     #import "video_macosx.h" // For init_* globals, and bitmap drawing strategy
33    
34     #import "EmulatorView.h"
35    
36     @implementation EmulatorView
37    
38    
39     //
40     // Standard NSView methods that we override
41     //
42    
43     - (id) initWithFrame: (NSRect) frameRect
44     {
45     self = [super initWithFrame: frameRect];
46    
47     output = self; // Set global for access by Basilisk C++ code
48     // bitmap = nil; // Set by readyToDraw:
49     drawView = NO; // Disable drawing until later
50 nigel 1.4 fullScreen = NO;
51 nigel 1.1
52     return self;
53     }
54    
55 nigel 1.8 - (void) awakeFromNib
56     {
57     // Here we store the height of the screen which the app was opened on.
58     // NSApplication's sendEvent: always uses that screen for its mouse co-ords
59     screen_height = (int) [[NSScreen mainScreen] frame].size.height;
60     }
61    
62    
63 nigel 1.5 // Mouse click in this window. If window is not active,
64     // should the click be passed to this view?
65 nigel 1.1 - (BOOL) acceptsFirstMouse: (NSEvent *) event
66     {
67     return [self mouseInView];
68     }
69    
70    
71 nigel 1.8 //
72     // Key event processing.
73     // OS X doesn't send us separate events for the modifier keys
74     // (shift/control/command), so we need to monitor them separately
75     //
76    
77 nigel 1.1 #include <adb.h>
78    
79     static int prevFlags;
80    
81     - (void) flagsChanged: (NSEvent *) event
82     {
83     int flags = [event modifierFlags];
84    
85     if ( (flags & NSAlphaShiftKeyMask) != (prevFlags & NSAlphaShiftKeyMask) )
86     if ( flags & NSAlphaShiftKeyMask )
87     ADBKeyDown(0x39); // CAPS_LOCK
88     else
89     ADBKeyUp(0x39);
90    
91     if ( (flags & NSShiftKeyMask) != (prevFlags & NSShiftKeyMask) )
92     if ( flags & NSShiftKeyMask )
93     ADBKeyDown(0x38); // SHIFT_LEFT
94     else
95     ADBKeyUp(0x38);
96    
97     if ( (flags & NSControlKeyMask) != (prevFlags & NSControlKeyMask) )
98     if ( flags & NSControlKeyMask )
99     ADBKeyDown(0x36); // CTL_LEFT
100     else
101     ADBKeyUp(0x36);
102    
103     if ( (flags & NSAlternateKeyMask) != (prevFlags & NSAlternateKeyMask) )
104     if ( flags & NSAlternateKeyMask )
105     ADBKeyDown(0x3a); // OPTION_LEFT
106     else
107     ADBKeyUp(0x3a);
108    
109     if ( (flags & NSCommandKeyMask) != (prevFlags & NSCommandKeyMask) )
110     if ( flags & NSCommandKeyMask )
111     ADBKeyDown(0x37); // APPLE_LEFT
112     else
113     ADBKeyUp(0x37);
114    
115     prevFlags = flags;
116     }
117    
118 nigel 1.8 //
119     // Windowed mode. We only send mouse/key events
120     // if the OS X mouse is within the little screen
121     //
122 nigel 1.1 - (BOOL) mouseInView: (NSEvent *) event
123     {
124 nigel 1.4 NSRect box;
125 nigel 1.5 NSPoint loc;
126    
127 nigel 1.4 if ( fullScreen )
128 nigel 1.5 {
129     box = displayBox;
130     loc = [NSEvent mouseLocation];
131     }
132 nigel 1.4 else
133 nigel 1.5 {
134 nigel 1.4 box = [self frame];
135 nigel 1.5 loc = [event locationInWindow];
136     }
137    
138     D(NSLog (@"%s - loc.x=%f, loc.y=%f, box.origin.x=%f, box.origin.y=%f, box.size.width=%f, box.size.height=%f", __PRETTY_FUNCTION__, loc.x, loc.y, box.origin.x, box.origin.y, box.size.width, box.size.height));
139 nigel 1.1 return [self mouse: loc inRect: box];
140     }
141    
142     - (BOOL) mouseInView
143     {
144     NSPoint loc = [[self window] mouseLocationOutsideOfEventStream];
145     NSRect box = [self frame];
146     D(NSLog (@"%s - loc.x=%f, loc.y=%f, box.origin.x=%f, box.origin.y=%f",
147     __PRETTY_FUNCTION__, loc.x, loc.y, box.origin.x, box.origin.y));
148     return [self mouse: loc inRect: box];
149     }
150    
151     //
152     // Custom methods
153     //
154    
155     - (void) benchmark
156     {
157     int i;
158     float seconds;
159     NSDate *startDate;
160 nigel 1.9 char *method;
161 nigel 1.1
162     if ( ! drawView )
163 nigel 1.9 {
164     WarningSheet (@"The emulator has not been setup yet.",
165     @"Try to run, then pause the emulator, first.", nil, [self window]);
166 nigel 1.1 return;
167 nigel 1.9 }
168 nigel 1.1
169     drawView = NO;
170     [self lockFocus];
171     startDate = [NSDate date];
172     for (i = 1; i < 300; ++i )
173     #ifdef NSBITMAP
174     [bitmap draw];
175     #endif
176     #ifdef CGIMAGEREF
177 nigel 1.7 cgDrawInto([self bounds], cgImgRep);
178 nigel 1.1 #endif
179     #ifdef CGDRAWBITMAP
180     [self CGDrawBitmap];
181     #endif
182     seconds = -[startDate timeIntervalSinceNow];
183     [self unlockFocus];
184     drawView = YES;
185    
186 nigel 1.9 #ifdef NSBITMAP
187     method = "NSBITMAP";
188     #endif
189     #ifdef CGIMAGEREF
190     method = "CGIMAGEREF";
191     #endif
192     #ifdef CGDRAWBITMAP
193     method = "CGDRAWBITMAP";
194     #endif
195    
196     InfoSheet(@"Ran benchmark (300 screen redraws)",
197 nigel 1.1 [NSString stringWithFormat:
198 nigel 1.9 @"%.2f seconds, %.3f frames per second (using %s implementation)",
199     seconds, i/seconds, method],
200 nigel 1.1 @"Thanks", [self window]);
201     }
202    
203     // Return a TIFF for a snapshot of the screen image
204     - (NSData *) TIFFrep
205     {
206     #ifdef NSBITMAP
207     return [bitmap TIFFRepresentation];
208     #else
209 nigel 1.9 NSBitmapImageRep *b = [NSBitmapImageRep alloc];
210    
211     b = [b initWithBitmapDataPlanes: (unsigned char **) &bitmap
212     pixelsWide: x
213     pixelsHigh: y
214     #ifdef CGIMAGEREF
215     bitsPerSample: CGImageGetBitsPerComponent(cgImgRep)
216     samplesPerPixel: 3
217     hasAlpha: NO
218     isPlanar: NO
219     colorSpaceName: NSCalibratedRGBColorSpace
220     bytesPerRow: CGImageGetBytesPerRow(cgImgRep)
221     bitsPerPixel: CGImageGetBitsPerPixel(cgImgRep)];
222     #endif
223     #ifdef CGDRAWBITMAP
224     bitsPerSample: bps
225     samplesPerPixel: spp
226     hasAlpha: hasAlpha
227     isPlanar: isPlanar
228     colorSpaceName: NSCalibratedRGBColorSpace
229     bytesPerRow: bytesPerRow
230     bitsPerPixel: bpp];
231     #endif
232    
233     if ( ! b )
234     {
235 nigel 1.15 ErrorAlert("Could not allocate an NSBitmapImageRep for the TIFF\nTry setting the emulation to millions of colours?");
236 nigel 1.9 return nil;
237     }
238    
239     return [b TIFFRepresentation];
240 nigel 1.1 #endif
241     }
242    
243     // Enable display of, and drawing into, the view
244     #ifdef NSBITMAP
245     - (void) readyToDraw: (NSBitmapImageRep *) theBitmap
246     imageWidth: (short) width
247     imageHeight: (short) height
248     {
249 nigel 1.7 numBytes = [theBitmap bytesPerRow] * height;
250 nigel 1.1 #endif
251     #ifdef CGIMAGEREF
252     - (void) readyToDraw: (CGImageRef) image
253 nigel 1.9 bitmap: (void *) theBitmap
254 nigel 1.1 imageWidth: (short) width
255     imageHeight: (short) height
256     {
257 nigel 1.7 cgImgRep = image;
258 nigel 1.8 numBytes = CGImageGetBytesPerRow(image) * height;
259 nigel 1.1 #endif
260     #ifdef CGDRAWBITMAP
261     - (void) readyToDraw: (void *) theBitmap
262     width: (short) width
263     height: (short) height
264     bps: (short) bitsPerSample
265     spp: (short) samplesPerPixel
266     bpp: (short) bitsPerPixel
267     bpr: (int) bpr
268     isPlanar: (BOOL) planar
269     hasAlpha: (BOOL) alpha
270     {
271     bps = bitsPerSample;
272     spp = samplesPerPixel;
273     bpp = bitsPerPixel;
274     bytesPerRow = bpr;
275     isPlanar = planar;
276     hasAlpha = alpha;
277 nigel 1.7 numBytes = bpr * height;
278 nigel 1.1 #endif
279 nigel 1.9 D(NSLog(@"readyToDraw: theBitmap=%lx\n", theBitmap));
280    
281     bitmap = theBitmap;
282 nigel 1.1 x = width, y = height;
283     drawView = YES;
284     [[self window] setAcceptsMouseMovedEvents: YES];
285     // [[self window] setInitialFirstResponder: self];
286     [[self window] makeFirstResponder: self];
287     }
288    
289     - (void) disableDrawing
290     {
291     drawView = NO;
292     }
293    
294 nigel 1.5 - (void) startedFullScreen: (CGDirectDisplayID) display
295 nigel 1.4 {
296 nigel 1.5 CGRect displayBounds = CGDisplayBounds(display);
297    
298 nigel 1.4 fullScreen = YES;
299 nigel 1.5 memcpy(&displayBox, &displayBounds, sizeof(displayBox));
300 nigel 1.4 }
301    
302 nigel 1.1 - (short) width
303     {
304     return (short)[self bounds].size.width;
305     }
306    
307     - (short) height
308     {
309     return (short)[self bounds].size.height;
310     }
311    
312 nigel 1.4 - (BOOL) isFullScreen
313     {
314     return fullScreen;
315     }
316    
317 nigel 1.1 - (BOOL) isOpaque
318     {
319     return drawView;
320     }
321    
322     - (BOOL) processKeyEvent: (NSEvent *) event
323     {
324 nigel 1.4 if ( fullScreen || [self acceptsFirstMouse: event] )
325 nigel 1.1 if ( [event isARepeat] )
326     return NO;
327     else
328     return YES;
329    
330     [self interpretKeyEvents:[NSArray arrayWithObject:event]];
331     return NO;
332     }
333    
334     - (void) keyDown: (NSEvent *) event
335     {
336     if ( [self processKeyEvent: event] )
337 nigel 1.2 {
338     int code = [event keyCode];
339    
340     if ( code == 126 ) code = 0x3e; // CURS_UP
341     if ( code == 125 ) code = 0x3d; // CURS_DOWN
342     if ( code == 124 ) code = 0x3c; // CURS_RIGHT
343     if ( code == 123 ) code = 0x3b; // CURS_LEFT
344    
345     ADBKeyDown(code);
346     }
347 nigel 1.1 }
348    
349     - (void) keyUp: (NSEvent *) event
350     {
351     if ( [self processKeyEvent: event] )
352 nigel 1.2 {
353     int code = [event keyCode];
354    
355     if ( code == 126 ) code = 0x3e; // CURS_UP
356     if ( code == 125 ) code = 0x3d; // CURS_DOWN
357     if ( code == 124 ) code = 0x3c; // CURS_RIGHT
358     if ( code == 123 ) code = 0x3b; // CURS_LEFT
359    
360     ADBKeyUp(code);
361     }
362 nigel 1.1 }
363    
364 nigel 1.8
365     - (void) fullscreenMouseMove
366     {
367     NSPoint location = [NSEvent mouseLocation];
368    
369     D(NSLog (@"%s - loc.x=%f, loc.y=%f",
370     __PRETTY_FUNCTION__, location.x, location.y));
371     D(NSLog (@"%s - Sending ADBMouseMoved(%d,%d). (%d-%d)",
372     __PRETTY_FUNCTION__, (int)location.x,
373     screen_height - (int)location.y, screen_height, (int)location.y));
374     ADBMouseMoved((int)location.x, screen_height - (int)location.y);
375     }
376    
377 nigel 1.1 static NSPoint mouse; // Previous/current mouse location
378    
379     - (BOOL) processMouseMove: (NSEvent *) event
380     {
381 nigel 1.13 if ( ! drawView )
382     {
383     D(NSLog(@"Unable to process event - Emulator has not started yet"));
384     return NO;
385     }
386 nigel 1.1
387 nigel 1.4 if ( fullScreen )
388 nigel 1.8 {
389     [self fullscreenMouseMove];
390     return YES;
391     }
392    
393 nigel 1.13 NSPoint location = [self convertPoint: [event locationInWindow] fromView:nil];
394 nigel 1.8
395     D(NSLog (@"%s - loc.x=%f, loc.y=%f",
396     __PRETTY_FUNCTION__, location.x, location.y));
397 nigel 1.1
398 nigel 1.5 if ( NSEqualPoints(location, mouse) )
399 nigel 1.1 return NO;
400    
401 nigel 1.5 mouse = location;
402 nigel 1.1
403 nigel 1.11 int mouseY = y - (int) (y * mouse.y / [self height]);
404     int mouseX = (int) (x * mouse.x / [self width]);
405 nigel 1.10 // If the view was not resizable, then this would be simpler:
406     // int mouseY = y - (int) mouse.y;
407     // int mouseX = (int) mouse.x;
408 nigel 1.1
409     ADBMouseMoved(mouseX, mouseY);
410     return YES;
411     }
412    
413     - (void) mouseDown: (NSEvent *) event
414     {
415     [self processMouseMove: event];
416     ADBMouseDown(0);
417     }
418    
419     - (void) mouseDragged: (NSEvent *) event
420     {
421     [self processMouseMove: event];
422     }
423    
424     - (void) mouseMoved: (NSEvent *) event
425     {
426     #if DEBUG
427     if ( ! [self mouseInView] )
428     {
429     NSLog (@"%s - Received event while outside of view", __PRETTY_FUNCTION__);
430     return;
431     }
432     #endif
433     [self processMouseMove: event];
434     }
435    
436     - (void) mouseUp: (NSEvent *) event
437     {
438     [self processMouseMove: event];
439     ADBMouseUp(0);
440     }
441    
442 nigel 1.9 #if DEBUG
443 nigel 1.1 - (void) randomise // Draw some coloured snow in the bitmap
444     {
445 nigel 1.7 unsigned char *data,
446     *pixel;
447    
448     #ifdef NSBITMAP
449     data = [bitmap bitmapData];
450 nigel 1.9 #else
451     data = bitmap;
452 nigel 1.7 #endif
453 nigel 1.1
454     for ( int i = 0; i < 1000; ++i )
455     {
456 nigel 1.7 pixel = data + (int) (numBytes * rand() / RAND_MAX);
457 nigel 1.1 *pixel = (unsigned char) (256.0 * rand() / RAND_MAX);
458     }
459     }
460     #endif
461    
462     - (void) drawRect: (NSRect) rect
463     {
464     if ( ! drawView ) // If the emulator is still being setup,
465     return; // we do not want to draw
466    
467     #if DEBUG
468     NSLog(@"In drawRect");
469 nigel 1.7 [self randomise];
470 nigel 1.1 #endif
471    
472     #ifdef NSBITMAP
473     NSRectClip(rect);
474     [bitmap draw];
475     #endif
476     #ifdef CGIMAGEREF
477 nigel 1.7 cgDrawInto(rect, cgImgRep);
478 nigel 1.1 #endif
479     #ifdef CGDRAWBITMAP
480     [self CGDrawBitmap];
481     #endif
482 nigel 1.10 }
483    
484     - (void) setTo: (int) val // Set all of bitmap to val
485     {
486     unsigned char *data
487     #ifdef NSBITMAP
488     = [bitmap bitmapData];
489     #else
490 nigel 1.11 = (unsigned char *) bitmap;
491 nigel 1.10 #endif
492    
493 nigel 1.11 memset(data, val, (long unsigned)numBytes);
494 nigel 1.10 }
495    
496     - (void) blacken // Set bitmap black
497     {
498     [self setTo: 0];
499     }
500    
501     - (void) clear // Set bitmap white
502     {
503     [self setTo: 0xFF];
504 nigel 1.1 }
505    
506     //
507     // Extra drawing stuff
508     //
509    
510     #ifdef CGDRAWBITMAP
511 nigel 1.6 extern "C" void CGDrawBitmap(...);
512 nigel 1.1
513     - (void) CGDrawBitmap
514     {
515 nigel 1.7 CGContextRef cgContext = (CGContextRef) [[NSGraphicsContext currentContext]
516     graphicsPort];
517 nigel 1.1 NSRect rect = [self bounds];
518     CGRect cgRect = {
519     {rect.origin.x, rect.origin.y},
520     {rect.size.width, rect.size.height}
521     };
522    
523     CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
524    
525    
526 nigel 1.7 // CGContextSetShouldAntialias(cgContext, NO); // Seems to have no effect?
527 nigel 1.1
528     CGDrawBitmap(cgContext, cgRect, x, y, bps, spp, bpp,
529     bytesPerRow, isPlanar, hasAlpha, colourSpace, &bitmap);
530     }
531     #endif
532    
533     #ifdef CGIMAGEREF
534     void
535 nigel 1.7 cgDrawInto(NSRect rect, CGImageRef cgImgRep)
536 nigel 1.1 {
537 nigel 1.7 CGContextRef cgContext = (CGContextRef) [[NSGraphicsContext currentContext]
538     graphicsPort];
539 nigel 1.1 CGRect cgRect = {
540     {rect.origin.x, rect.origin.y},
541     {rect.size.width, rect.size.height}
542     };
543    
544 nigel 1.7 // CGContextSetShouldAntialias(cgContext, NO); // Seems to have no effect?
545 nigel 1.1
546 nigel 1.7 CGContextDrawImage(cgContext, cgRect, cgImgRep);
547 nigel 1.1 }
548     #endif
549    
550 nigel 1.3 @end