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

# Content
1 /*
2 * EmulatorView.mm - Custom NSView for Basilisk II windowed graphics output
3 *
4 * $Id: EmulatorView.mm,v 1.14 2005/01/30 21:42:13 gbeauche Exp $
5 *
6 * Basilisk II (C) 1997-2005 Christian Bauer
7 *
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 fullScreen = NO;
51
52 return self;
53 }
54
55 - (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 // Mouse click in this window. If window is not active,
64 // should the click be passed to this view?
65 - (BOOL) acceptsFirstMouse: (NSEvent *) event
66 {
67 return [self mouseInView];
68 }
69
70
71 //
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 #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 //
119 // Windowed mode. We only send mouse/key events
120 // if the OS X mouse is within the little screen
121 //
122 - (BOOL) mouseInView: (NSEvent *) event
123 {
124 NSRect box;
125 NSPoint loc;
126
127 if ( fullScreen )
128 {
129 box = displayBox;
130 loc = [NSEvent mouseLocation];
131 }
132 else
133 {
134 box = [self frame];
135 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 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 char *method;
161
162 if ( ! drawView )
163 {
164 WarningSheet (@"The emulator has not been setup yet.",
165 @"Try to run, then pause the emulator, first.", nil, [self window]);
166 return;
167 }
168
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 cgDrawInto([self bounds], cgImgRep);
178 #endif
179 #ifdef CGDRAWBITMAP
180 [self CGDrawBitmap];
181 #endif
182 seconds = -[startDate timeIntervalSinceNow];
183 [self unlockFocus];
184 drawView = YES;
185
186 #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 [NSString stringWithFormat:
198 @"%.2f seconds, %.3f frames per second (using %s implementation)",
199 seconds, i/seconds, method],
200 @"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 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 ErrorAlert("Could not allocate an NSBitmapImageRep for the TIFF\nTry setting the emulation to millions of colours?");
236 return nil;
237 }
238
239 return [b TIFFRepresentation];
240 #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 numBytes = [theBitmap bytesPerRow] * height;
250 #endif
251 #ifdef CGIMAGEREF
252 - (void) readyToDraw: (CGImageRef) image
253 bitmap: (void *) theBitmap
254 imageWidth: (short) width
255 imageHeight: (short) height
256 {
257 cgImgRep = image;
258 numBytes = CGImageGetBytesPerRow(image) * height;
259 #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 numBytes = bpr * height;
278 #endif
279 D(NSLog(@"readyToDraw: theBitmap=%lx\n", theBitmap));
280
281 bitmap = theBitmap;
282 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 - (void) startedFullScreen: (CGDirectDisplayID) display
295 {
296 CGRect displayBounds = CGDisplayBounds(display);
297
298 fullScreen = YES;
299 memcpy(&displayBox, &displayBounds, sizeof(displayBox));
300 }
301
302 - (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 - (BOOL) isFullScreen
313 {
314 return fullScreen;
315 }
316
317 - (BOOL) isOpaque
318 {
319 return drawView;
320 }
321
322 - (BOOL) processKeyEvent: (NSEvent *) event
323 {
324 if ( fullScreen || [self acceptsFirstMouse: event] )
325 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 {
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 }
348
349 - (void) keyUp: (NSEvent *) event
350 {
351 if ( [self processKeyEvent: event] )
352 {
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 }
363
364
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 static NSPoint mouse; // Previous/current mouse location
378
379 - (BOOL) processMouseMove: (NSEvent *) event
380 {
381 if ( ! drawView )
382 {
383 D(NSLog(@"Unable to process event - Emulator has not started yet"));
384 return NO;
385 }
386
387 if ( fullScreen )
388 {
389 [self fullscreenMouseMove];
390 return YES;
391 }
392
393 NSPoint location = [self convertPoint: [event locationInWindow] fromView:nil];
394
395 D(NSLog (@"%s - loc.x=%f, loc.y=%f",
396 __PRETTY_FUNCTION__, location.x, location.y));
397
398 if ( NSEqualPoints(location, mouse) )
399 return NO;
400
401 mouse = location;
402
403 int mouseY = y - (int) (y * mouse.y / [self height]);
404 int mouseX = (int) (x * mouse.x / [self width]);
405 // 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
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 #if DEBUG
443 - (void) randomise // Draw some coloured snow in the bitmap
444 {
445 unsigned char *data,
446 *pixel;
447
448 #ifdef NSBITMAP
449 data = [bitmap bitmapData];
450 #else
451 data = bitmap;
452 #endif
453
454 for ( int i = 0; i < 1000; ++i )
455 {
456 pixel = data + (int) (numBytes * rand() / RAND_MAX);
457 *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 [self randomise];
470 #endif
471
472 #ifdef NSBITMAP
473 NSRectClip(rect);
474 [bitmap draw];
475 #endif
476 #ifdef CGIMAGEREF
477 cgDrawInto(rect, cgImgRep);
478 #endif
479 #ifdef CGDRAWBITMAP
480 [self CGDrawBitmap];
481 #endif
482 }
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 = (unsigned char *) bitmap;
491 #endif
492
493 memset(data, val, (long unsigned)numBytes);
494 }
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 }
505
506 //
507 // Extra drawing stuff
508 //
509
510 #ifdef CGDRAWBITMAP
511 extern "C" void CGDrawBitmap(...);
512
513 - (void) CGDrawBitmap
514 {
515 CGContextRef cgContext = (CGContextRef) [[NSGraphicsContext currentContext]
516 graphicsPort];
517 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 // CGContextSetShouldAntialias(cgContext, NO); // Seems to have no effect?
527
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 cgDrawInto(NSRect rect, CGImageRef cgImgRep)
536 {
537 CGContextRef cgContext = (CGContextRef) [[NSGraphicsContext currentContext]
538 graphicsPort];
539 CGRect cgRect = {
540 {rect.origin.x, rect.origin.y},
541 {rect.size.width, rect.size.height}
542 };
543
544 // CGContextSetShouldAntialias(cgContext, NO); // Seems to have no effect?
545
546 CGContextDrawImage(cgContext, cgRect, cgImgRep);
547 }
548 #endif
549
550 @end