ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/MacOSX/EmulatorView.mm
Revision: 1.10
Committed: 2003-03-26T23:37:42Z (21 years, 2 months ago) by nigel
Branch: MAIN
CVS Tags: nigel-build-13
Changes since 1.9: +26 -6 lines
Log Message:
1) Publish methods for clearing and blackening the bitmap
2) Always do the calculations for a possibly scaled view.
Even if we prevent the user from changing the view size,
the Monitors control panel in the emulator may still try to
select a resolution which is too large for the screen,
and thus the window will have to be reduced to fit,
and thus the screen and bitmap co-ords may not match

File Contents

# Content
1 /*
2 * EmulatorView.mm - Custom NSView for Basilisk II windowed graphics output
3 *
4 * $Id: EmulatorView.mm,v 1.9 2003/03/21 06:41:04 nigel Exp $
5 *
6 * Basilisk II (C) 1997-2003 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");
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 NSPoint location;
382
383 if ( fullScreen )
384 {
385 [self fullscreenMouseMove];
386 return YES;
387 }
388
389 location = [self convertPoint: [event locationInWindow] fromView:nil];
390
391 D(NSLog (@"%s - loc.x=%f, loc.y=%f",
392 __PRETTY_FUNCTION__, location.x, location.y));
393
394 if ( NSEqualPoints(location, mouse) )
395 return NO;
396
397 mouse = location;
398
399 int mouseY = y - y * mouse.y / [self height];
400 int mouseX = x * mouse.x / [self width];
401 // If the view was not resizable, then this would be simpler:
402 // int mouseY = y - (int) mouse.y;
403 // int mouseX = (int) mouse.x;
404
405 ADBMouseMoved(mouseX, mouseY);
406 return YES;
407 }
408
409 - (void) mouseDown: (NSEvent *) event
410 {
411 [self processMouseMove: event];
412 ADBMouseDown(0);
413 }
414
415 - (void) mouseDragged: (NSEvent *) event
416 {
417 [self processMouseMove: event];
418 }
419
420 - (void) mouseMoved: (NSEvent *) event
421 {
422 #if DEBUG
423 if ( ! [self mouseInView] )
424 {
425 NSLog (@"%s - Received event while outside of view", __PRETTY_FUNCTION__);
426 return;
427 }
428 #endif
429 [self processMouseMove: event];
430 }
431
432 - (void) mouseUp: (NSEvent *) event
433 {
434 [self processMouseMove: event];
435 ADBMouseUp(0);
436 }
437
438 #if DEBUG
439 - (void) randomise // Draw some coloured snow in the bitmap
440 {
441 unsigned char *data,
442 *pixel;
443
444 #ifdef NSBITMAP
445 data = [bitmap bitmapData];
446 #else
447 data = bitmap;
448 #endif
449
450 for ( int i = 0; i < 1000; ++i )
451 {
452 pixel = data + (int) (numBytes * rand() / RAND_MAX);
453 *pixel = (unsigned char) (256.0 * rand() / RAND_MAX);
454 }
455 }
456 #endif
457
458 - (void) drawRect: (NSRect) rect
459 {
460 if ( ! drawView ) // If the emulator is still being setup,
461 return; // we do not want to draw
462
463 #if DEBUG
464 NSLog(@"In drawRect");
465 [self randomise];
466 #endif
467
468 #ifdef NSBITMAP
469 NSRectClip(rect);
470 [bitmap draw];
471 #endif
472 #ifdef CGIMAGEREF
473 cgDrawInto(rect, cgImgRep);
474 #endif
475 #ifdef CGDRAWBITMAP
476 [self CGDrawBitmap];
477 #endif
478 }
479
480 - (void) setTo: (int) val // Set all of bitmap to val
481 {
482 unsigned char *data
483 #ifdef NSBITMAP
484 = [bitmap bitmapData];
485 #else
486 = bitmap;
487 #endif
488
489 memset(data, val, numBytes);
490 }
491
492 - (void) blacken // Set bitmap black
493 {
494 [self setTo: 0];
495 }
496
497 - (void) clear // Set bitmap white
498 {
499 [self setTo: 0xFF];
500 }
501
502 //
503 // Extra drawing stuff
504 //
505
506 #ifdef CGDRAWBITMAP
507 extern "C" void CGDrawBitmap(...);
508
509 - (void) CGDrawBitmap
510 {
511 CGContextRef cgContext = (CGContextRef) [[NSGraphicsContext currentContext]
512 graphicsPort];
513 NSRect rect = [self bounds];
514 CGRect cgRect = {
515 {rect.origin.x, rect.origin.y},
516 {rect.size.width, rect.size.height}
517 };
518
519 CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
520
521
522 // CGContextSetShouldAntialias(cgContext, NO); // Seems to have no effect?
523
524 CGDrawBitmap(cgContext, cgRect, x, y, bps, spp, bpp,
525 bytesPerRow, isPlanar, hasAlpha, colourSpace, &bitmap);
526 }
527 #endif
528
529 #ifdef CGIMAGEREF
530 void
531 cgDrawInto(NSRect rect, CGImageRef cgImgRep)
532 {
533 CGContextRef cgContext = (CGContextRef) [[NSGraphicsContext currentContext]
534 graphicsPort];
535 CGRect cgRect = {
536 {rect.origin.x, rect.origin.y},
537 {rect.size.width, rect.size.height}
538 };
539
540 // CGContextSetShouldAntialias(cgContext, NO); // Seems to have no effect?
541
542 CGContextDrawImage(cgContext, cgRect, cgImgRep);
543 }
544 #endif
545
546 @end