Line data Source code
1 0 : /*
2 :
3 : MyOpenGLView.m
4 :
5 : Oolite
6 : Copyright (C) 2004-2013 Giles C Williams and contributors
7 :
8 : This program is free software; you can redistribute it and/or
9 : modify it under the terms of the GNU General Public License
10 : as published by the Free Software Foundation; either version 2
11 : of the License, or (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., 51 Franklin Street, Fifth Floor, Boston,
21 : MA 02110-1301, USA.
22 :
23 : */
24 :
25 : #import "MyOpenGLView.h"
26 :
27 : #import "GameController.h"
28 : #import "Universe.h"
29 : #import "Entity.h"
30 : #import "OOPlanetEntity.h"
31 : #import "ResourceManager.h"
32 : #import "GuiDisplayGen.h"
33 : #import <Carbon/Carbon.h>
34 : #import "NSFileManagerOOExtensions.h"
35 : #import "OOGraphicsResetManager.h"
36 : #import "PlayerEntity.h"
37 :
38 : #ifndef NDEBUG
39 : #import <Foundation/NSDebug.h>
40 : #endif
41 :
42 :
43 0 : static NSString * kOOLogKeyCodeOutOfRange = @"input.keyMapping.codeOutOfRange";
44 0 : static NSString * kOOLogKeyUp = @"input.keyMapping.keyPress.keyUp";
45 0 : static NSString * kOOLogKeyDown = @"input.keyMapping.keyPress.keyDown";
46 :
47 :
48 0 : static void GetDesiredCursorState(OOMouseInteractionMode mode, BOOL *outHidden, BOOL *outObscured);
49 0 : static void ApplyCursorState(OOMouseInteractionMode mode);
50 0 : static void UnapplyCursorState(OOMouseInteractionMode mode);
51 :
52 :
53 : @interface MyOpenGLView(Internal)
54 :
55 0 : - (int) translateKeyCode:(int)input;
56 :
57 0 : - (void) recenterVirtualJoystick;
58 :
59 : @end
60 :
61 :
62 : #if !OOLITE_MAC_OS_X_10_7
63 : @interface NSView (Lion)
64 :
65 0 : - (BOOL) wantsBestResolutionOpenGLSurface;
66 0 : - (void) setWantsBestResolutionOpenGLSurface:(BOOL)flag;
67 :
68 0 : - (NSPoint) convertPointToBacking:(NSPoint)aPoint;
69 0 : - (NSPoint) convertPointFromBacking:(NSPoint)aPoint;
70 0 : - (NSSize) convertSizeToBacking:(NSSize)aSize;
71 0 : - (NSSize) convertSizeFromBacking:(NSSize)aSize;
72 0 : - (NSRect) convertRectToBacking:(NSRect)aRect;
73 0 : - (NSRect) convertRectFromBacking:(NSRect)aRect;
74 :
75 : @end
76 : #endif
77 :
78 :
79 : @implementation MyOpenGLView
80 :
81 0 : - (id) initWithFrame:(NSRect)frameRect
82 : {
83 : #ifndef NDEBUG
84 : if (NSZombieEnabled)
85 : {
86 : OOLog(@"debug.zombieEnabled", @"*** ZOMBIES WILL EAT YOUR BRAIN ***");
87 : }
88 : #endif
89 :
90 : // Pixel Format Attributes for the View-based (non-FullScreen) NSOpenGLContext
91 : NSOpenGLPixelFormatAttribute attrs[] =
92 : {
93 : // Specify that we want a windowed OpenGL context.
94 : // Must be first or we'll hit an assert in the legacy fullscreen controller.
95 : NSOpenGLPFAWindow,
96 :
97 : // We may be on a multi-display system (and each screen may be driven by a different renderer), so we need to specify which screen we want to take over.
98 : // For this demo, we'll specify the main screen.
99 : NSOpenGLPFAScreenMask, CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay),
100 :
101 : // Specifying "NoRecovery" gives us a context that cannot fall back to the software renderer.
102 : // This makes the View-based context a compatible with the fullscreen context, enabling us to use the "shareContext"
103 : // feature to share textures, display lists, and other OpenGL objects between the two.
104 : NSOpenGLPFANoRecovery,
105 :
106 : // Attributes Common to FullScreen and non-FullScreen
107 : NSOpenGLPFACompliant,
108 :
109 : NSOpenGLPFAColorSize, 32,
110 : NSOpenGLPFADepthSize, 32,
111 : NSOpenGLPFADoubleBuffer,
112 : NSOpenGLPFAAccelerated,
113 : #if FSAA
114 : // Need a preference or other sane way to activate this
115 : NSOpenGLPFAMultisample,
116 : NSOpenGLPFASampleBuffers, 1,
117 : NSOpenGLPFASamples,4,
118 : #endif
119 : 0
120 : };
121 :
122 : // Create our non-FullScreen pixel format.
123 : NSOpenGLPixelFormat *pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease];
124 :
125 : if ((self = [super initWithFrame:frameRect pixelFormat:pixelFormat]))
126 : {
127 : if ([self respondsToSelector:@selector(setAcceptsTouchEvents:)])
128 : {
129 : [self setAcceptsTouchEvents:YES];
130 : }
131 :
132 : if ([self respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)])
133 : {
134 : // Enable high resolution on Retina displays.
135 : [self setWantsBestResolutionOpenGLSurface:YES];
136 : }
137 :
138 : matrixManager = [[OOOpenGLMatrixManager alloc] init];
139 : _pixelFormatAttributes = [[NSData alloc] initWithBytes:attrs length:sizeof attrs];
140 : virtualJoystickPosition = NSMakePoint(0.0,0.0);
141 :
142 : typedString = [[NSMutableString alloc] initWithString:@""];
143 : allowingStringInput = gvStringInputNo;
144 : isAlphabetKeyDown = NO;
145 :
146 : timeIntervalAtLastClick = [NSDate timeIntervalSinceReferenceDate];
147 :
148 : _virtualScreen = [[self openGLContext] currentVirtualScreen];
149 : }
150 :
151 : // preload the printscreen key into our translation array because SDLK_PRINTSCREEN isn't available
152 : scancode2Unicode[55] = gvPrintScreenKey;
153 : [self initKeyMappingData];
154 :
155 : return self;
156 : }
157 :
158 : - (void) initKeyMappingData
159 : {
160 : NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
161 : NSDictionary *kmap = [NSDictionary dictionaryWithDictionary:[ResourceManager dictionaryFromFilesNamed:@"keymappings_mac.plist" inFolder:@"Config" mergeMode:MERGE_BASIC cache:NO]];
162 : // get the stored keyboard code from preferences
163 : NSString *kbd = [prefs oo_stringForKey:@"keyboard-code" defaultValue:@"default"];
164 : NSDictionary *subset = [kmap objectForKey:kbd];
165 :
166 : [keyMappings_normal release];
167 : keyMappings_normal = [[subset objectForKey:@"mapping_normal"] copy];
168 : [keyMappings_shifted release];
169 : keyMappings_shifted = [[subset objectForKey:@"mapping_shifted"] copy];
170 : }
171 :
172 :
173 0 : - (void) dealloc
174 : {
175 : DESTROY(typedString);
176 : DESTROY(_pixelFormatAttributes);
177 : DESTROY(matrixManager);
178 :
179 : if (keyMappings_normal)
180 : [keyMappings_normal release];
181 : if (keyMappings_shifted)
182 : [keyMappings_shifted release];
183 :
184 : [super dealloc];
185 : }
186 :
187 :
188 : - (void) setStringInput:(enum StringInput)value
189 : {
190 : allowingStringInput = value;
191 : }
192 :
193 :
194 : - (void) allowStringInput:(BOOL)value
195 : {
196 : if (value)
197 : {
198 : allowingStringInput = gvStringInputAlpha;
199 : }
200 : else
201 : {
202 : allowingStringInput = gvStringInputNo;
203 : }
204 : }
205 :
206 : - (enum StringInput) allowingStringInput
207 : {
208 : return allowingStringInput;
209 : }
210 :
211 :
212 : - (NSString *) typedString
213 : {
214 : return typedString;
215 : }
216 :
217 :
218 : - (void) resetTypedString
219 : {
220 : [typedString setString:@""];
221 : }
222 :
223 :
224 : - (void) setTypedString:(NSString *)value
225 : {
226 : [typedString setString:value];
227 : }
228 :
229 :
230 : - (NSSize) viewSize
231 : {
232 : return viewSize;
233 : }
234 :
235 :
236 : - (NSSize) backingViewSize
237 : {
238 : return backingViewSize;
239 : }
240 :
241 :
242 : - (GLfloat) display_z
243 : {
244 : return display_z;
245 : }
246 :
247 :
248 : - (GLfloat) x_offset
249 : {
250 : return x_offset;
251 : }
252 :
253 :
254 : - (GLfloat) y_offset
255 : {
256 : return y_offset;
257 : }
258 :
259 :
260 : - (GameController *) gameController
261 : {
262 : return gameController;
263 : }
264 :
265 :
266 : - (void) setGameController:(GameController *) controller
267 : {
268 : gameController = controller;
269 : }
270 :
271 :
272 : - (void) noteMouseInteractionModeChangedFrom:(OOMouseInteractionMode)oldMode to:(OOMouseInteractionMode)newMode
273 : {
274 : UnapplyCursorState(oldMode);
275 : ApplyCursorState(newMode);
276 : }
277 :
278 :
279 : - (void) updateScreen
280 : {
281 : if ([[self window] isVisible])
282 : {
283 : [self drawRect:NSMakeRect(0, 0, viewSize.width, viewSize.height)];
284 : }
285 : }
286 :
287 : - (void) pollControls
288 : {
289 : if ([NSDate timeIntervalSinceReferenceDate] > timeIntervalAtLastMouseWheel + OOMOUSEWHEEL_EVENTS_DELAY_INTERVAL)
290 : _mouseWheelDelta = 0.0f;
291 : }
292 :
293 : - (void) drawRect:(NSRect)rect
294 : {
295 : if ((viewSize.width != [self frame].size.width)||(viewSize.height != [self frame].size.height)) // resized
296 : {
297 : m_glContextInitialized = NO;
298 : viewSize = [self frame].size;
299 : }
300 :
301 : if (!m_glContextInitialized) [self initialiseGLWithSize:viewSize];
302 :
303 : // do all the drawing!
304 : if (UNIVERSE != nil) [UNIVERSE drawUniverse];
305 : else
306 : {
307 : // not set up yet, draw a black screen
308 : OOGL(glClearColor(0.0, 0.0, 0.0, 0.0));
309 : OOGL(glClear(GL_COLOR_BUFFER_BIT));
310 : }
311 :
312 : [[self openGLContext] flushBuffer];
313 : }
314 :
315 :
316 0 : - (void) noteMovedToBadDisplay
317 : {
318 : NSRunInformationalAlertPanel(DESC(@"oolite-mac-bad-display"), @"%@", nil, nil, nil, DESC(@"oolite-mac-bad-display-2"));
319 : }
320 :
321 :
322 0 : - (void) update
323 : {
324 : NSOpenGLContext *context = [self openGLContext];
325 :
326 : [context update];
327 : int virtualScreen = [context currentVirtualScreen];
328 : if (virtualScreen != _virtualScreen)
329 : {
330 : @try
331 : {
332 : [[OOGraphicsResetManager sharedManager] resetGraphicsState];
333 : _virtualScreen = virtualScreen;
334 : }
335 : @catch (NSException *exception)
336 : {
337 : /* Graphics reset failed, most likely because of OpenGL context
338 : incompatibility. Reset to previous "virtual screen" (i.e.,
339 : renderer). OS X's OpenGL implementation will take care of
340 : copying
341 : */
342 : [context setCurrentVirtualScreen:_virtualScreen];
343 : [[OOGraphicsResetManager sharedManager] resetGraphicsState]; // If this throws, we're screwed.
344 :
345 : if ([[self gameController] inFullScreenMode])
346 : {
347 : [[self gameController] pauseFullScreenModeToPerform:@selector(noteMovedToBadDisplay) onTarget:self];
348 : }
349 : else
350 : {
351 : [self noteMovedToBadDisplay];
352 : }
353 : }
354 : }
355 : }
356 :
357 :
358 : - (void) initialiseGLWithSize:(NSSize)v_size
359 : {
360 : viewSize = v_size;
361 : if (viewSize.width/viewSize.height > 4.0/3.0) {
362 : display_z = 480.0 * viewSize.width/viewSize.height;
363 : x_offset = 240.0 * viewSize.width/viewSize.height;
364 : y_offset = 240.0;
365 : } else {
366 : display_z = 640.0;
367 : x_offset = 320.0;
368 : y_offset = 320.0 * viewSize.height/viewSize.width;
369 : }
370 :
371 : if ([self respondsToSelector:@selector(convertSizeToBacking:)])
372 : {
373 : // High resolution mode support.
374 : backingViewSize = [self convertSizeToBacking:viewSize];
375 : }
376 : else
377 : {
378 : backingViewSize = viewSize;
379 : }
380 :
381 : [self openGLContext]; // Force lazy setup if needed.
382 : [[self gameController] setUpBasicOpenGLStateWithSize:v_size];
383 : [[self openGLContext] flushBuffer];
384 :
385 : m_glContextInitialized = YES;
386 : }
387 :
388 :
389 : - (NSData *)pixelFormatAttributes
390 : {
391 : return _pixelFormatAttributes;
392 : }
393 :
394 :
395 : #ifdef MAC_OS_X_VERSION_10_7 // If building against 10.7 SDK, where relevant symbols are defined...
396 : - (void) viewDidMoveToWindow
397 : {
398 : /* Subscribe to NSWindowDidChangeBackingPropertiesNotification on systems
399 : which support it (10.7 and later). This notification fires when the
400 : scale factor or colour space of the window's backing store changes.
401 : We use it to track scale factor changes.
402 : */
403 : if (&NSWindowDidChangeBackingPropertiesNotification != NULL && [self.window respondsToSelector:@selector(backingScaleFactor)])
404 : {
405 : [[NSNotificationCenter defaultCenter] addObserver:self
406 : selector:@selector(backingPropertiesChanged:)
407 : name:NSWindowDidChangeBackingPropertiesNotification
408 : object:self.window];
409 :
410 : // Also, ensure the initial state makes sense.
411 : [self backingPropertiesChanged:nil];
412 : }
413 : }
414 :
415 :
416 : - (void) backingPropertiesChanged:(NSNotification *)notification
417 : {
418 : GLSetDisplayScaleFactor(self.window.backingScaleFactor);
419 : }
420 : #endif
421 :
422 :
423 : - (BOOL) snapShot:(NSString *)filename
424 : {
425 : BOOL snapShotOK = YES;
426 : NSSize v_size = backingViewSize;
427 :
428 : int w = v_size.width;
429 : int h = v_size.height;
430 :
431 : if (w & 3)
432 : w = w + 4 - (w & 3);
433 :
434 : long nPixels = w * h + 1;
435 :
436 : unsigned char *red = (unsigned char *)malloc(nPixels);
437 : unsigned char *green = (unsigned char *)malloc(nPixels);
438 : unsigned char *blue = (unsigned char *)malloc(nPixels);
439 :
440 : // backup the previous directory
441 : NSString *originalDirectory = [[NSFileManager defaultManager] currentDirectoryPath];
442 : // use the snapshots directory
443 : NSString *snapshotsDirectory = [[[GameController sharedController] snapshotsURLCreatingIfNeeded:YES] path];
444 : if (![[NSFileManager defaultManager] changeCurrentDirectoryPath:snapshotsDirectory])
445 : {
446 : NSBeep();
447 : OOLog(@"savedSnapshot.defaultPath.chdir.failed", @"Could not navigate to %@", snapshotsDirectory);
448 : snapShotOK = NO;
449 : goto FAIL;
450 : }
451 :
452 : BOOL withFilename = (filename != nil);
453 : static unsigned imageNo = 0;
454 : unsigned tmpImageNo = 0;
455 : NSString *pathToPic = nil;
456 : NSString *baseName = @"oolite";
457 :
458 : if (withFilename)
459 : {
460 : baseName = filename;
461 : pathToPic = [filename stringByAppendingString:@".png"];
462 : }
463 : else
464 : {
465 : tmpImageNo = imageNo;
466 : }
467 :
468 : if (withFilename && [[NSFileManager defaultManager] fileExistsAtPath:pathToPic])
469 : {
470 : OOLog(@"screenshot.filenameExists", @"Snapshot \"%@.png\" already exists - adding numerical sequence.", pathToPic);
471 : pathToPic = nil;
472 : }
473 :
474 : if (pathToPic == nil)
475 : {
476 : do
477 : {
478 : tmpImageNo++;
479 : pathToPic = [NSString stringWithFormat:@"%@-%03d.png", baseName, tmpImageNo];
480 : } while ([[NSFileManager defaultManager] fileExistsAtPath:pathToPic]);
481 : }
482 :
483 : if (!withFilename)
484 : {
485 : imageNo = tmpImageNo;
486 : }
487 :
488 : OOLog(@"screenshot", @"Saved screen shot \"%@\" (%u x %u pixels).", pathToPic, w, h);
489 :
490 : NSBitmapImageRep* bitmapRep =
491 : [[NSBitmapImageRep alloc]
492 : initWithBitmapDataPlanes:NULL // --> let the class allocate it
493 : pixelsWide: w
494 : pixelsHigh: h
495 : bitsPerSample: 8 // each component is 8 bits (1 byte)
496 : samplesPerPixel: 3 // number of components (R, G, B)
497 : hasAlpha: NO // no transparency
498 : isPlanar: NO // data integrated into single plane
499 : colorSpaceName: NSDeviceRGBColorSpace
500 : bytesPerRow: 3*w // can no longer let the class figure it out
501 : bitsPerPixel: 24 // can no longer let the class figure it out
502 : ];
503 :
504 : unsigned char *pixels = [bitmapRep bitmapData];
505 :
506 : OOGL(glReadPixels(0,0, w,h, GL_RED, GL_UNSIGNED_BYTE, red));
507 : OOGL(glReadPixels(0,0, w,h, GL_GREEN, GL_UNSIGNED_BYTE, green));
508 : OOGL(glReadPixels(0,0, w,h, GL_BLUE, GL_UNSIGNED_BYTE, blue));
509 :
510 : int x,y;
511 : for (y = 0; y < h; y++)
512 : {
513 : long index = (h - y - 1)*w;
514 : for (x = 0; x < w; x++) // set bitmap pixels
515 : {
516 : *pixels++ = red[index];
517 : *pixels++ = green[index];
518 : *pixels++ = blue[index++];
519 : }
520 : }
521 :
522 : [[bitmapRep representationUsingType:NSPNGFileType properties:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSImageInterlaced, NULL]]
523 : writeToFile:pathToPic atomically:YES]; // save PNG representation of image
524 :
525 : // free allocated objects and memory
526 : [bitmapRep release];
527 :
528 : FAIL:
529 : free(red);
530 : free(green);
531 : free(blue);
532 :
533 : // return to the previous directory
534 : [[NSFileManager defaultManager] changeCurrentDirectoryPath:originalDirectory];
535 : return snapShotOK;
536 : }
537 :
538 :
539 : - (void) stringToClipboard:(NSString *)stringToCopy
540 : {
541 : NSPasteboard *clipBoard = [NSPasteboard generalPasteboard];
542 : [clipBoard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil];
543 : [clipBoard setString:stringToCopy forType:NSPasteboardTypeString];
544 : }
545 :
546 :
547 0 : - (BOOL) acceptsFirstResponder
548 : {
549 : return YES;
550 : }
551 :
552 :
553 0 : - (void) keyUp:(NSEvent *)theEvent
554 : {
555 : NSString *stringValue = nil;
556 : int key;
557 : int keycode;
558 :
559 : stringValue = [theEvent charactersIgnoringModifiers];
560 :
561 : /* Bug: exception when releasing accent key.
562 : Analysis: Dead keys (accents and similar) return an empty string.
563 : Fix: reject zero-length strings. This is the Wrong Thing - we should
564 : really be using KeyTranslate()/UCKeyTranslate() to find out what the
565 : string would be if you pressed the key and then space.
566 : -- Ahruman 20070714
567 : */
568 : if ([stringValue length] < 1) return;
569 :
570 : suppressKeys = NO;
571 :
572 : keycode = [theEvent keyCode] & 255;
573 : key = keycodetrans[keycode]; // retrieve the character we got for pressing the hardware at key location 'keycode'
574 :
575 : OOLog(kOOLogKeyUp, @"Key up: stringValue = \"%@\", keyCode = %d, key = %u", stringValue, keycode, key);
576 :
577 : // Special handling of command keys used in full-screen mode.
578 : if ([theEvent modifierFlags] & NSCommandKeyMask)
579 : {
580 : switch (key)
581 : {
582 : case 'q':
583 : commandQ = NO;
584 : break;
585 :
586 : case 'f':
587 : commandF = NO;
588 : break;
589 : }
590 : // Pass through to allow clearing of normal key as well.
591 : }
592 :
593 : /* HACK: treat f12 as alias to cmd-F for compatibility with helpful forum
594 : advice etc.
595 : */
596 : if (key == NSF12FunctionKey)
597 : {
598 : commandF = NO;
599 : f12 = NO;
600 : return;
601 : };
602 :
603 : isAlphabetKeyDown = NO;
604 : if ((key >= 0)&&(key < [self numKeys])&&(keys[key]))
605 : {
606 : keys[key] = NO;
607 : }
608 : else
609 : {
610 : if (key > [self numKeys])
611 : OOLog(kOOLogKeyCodeOutOfRange, @"Translated key: %d out of range", key);
612 : }
613 : }
614 :
615 :
616 0 : - (void) keyDown:(NSEvent *)theEvent
617 : {
618 : NSString *stringValue = nil;
619 : int key;
620 : int keycode;
621 :
622 : stringValue = [theEvent charactersIgnoringModifiers];
623 :
624 : /* Bug: exception when pressing accent key.
625 : Analysis: Dead keys (accents and similar) return an empty string.
626 : Fix: reject zero-length strings. This is the Wrong Thing - we should
627 : really be using KeyTranslate()/UCKeyTranslate() to find out what the
628 : string would be if you pressed the key and then space.
629 : -- Ahruman 20070714
630 : */
631 : //if ([stringValue length] < 1) return;
632 :
633 : keycode = [theEvent keyCode] & 255;
634 :
635 : if ([stringValue length] < 1)
636 : {
637 : if (!shift)
638 : {
639 : NSString *keyNormal = [keyMappings_normal objectForKey:[NSString stringWithFormat:@"%d", keycode]];
640 : if (keyNormal) stringValue = [NSString stringWithFormat:@"%c", [keyNormal integerValue]];
641 : }
642 : else
643 : {
644 : NSString *keyShifted = [keyMappings_shifted objectForKey:[NSString stringWithFormat:@"%d", keycode]];
645 : if (keyShifted) stringValue = [NSString stringWithFormat:@"%c", [keyShifted integerValue]];
646 : }
647 : // if we still have a zero-length string, return at this point
648 : if ([stringValue length] < 1) return;
649 : }
650 :
651 : key = [stringValue characterAtIndex:0];
652 : key = [self translateKeyCode:key];
653 :
654 : OOLog(kOOLogKeyDown, @"Key down: stringValue = \"%@\", keyCode = %d, key = %u", stringValue, keycode, key);
655 :
656 : // Special handling of command keys used in full-screen mode.
657 : if ([theEvent modifierFlags] & NSCommandKeyMask)
658 : {
659 : switch (key)
660 : {
661 : case 'q':
662 : commandQ = YES;
663 : break;
664 :
665 : case 'f':
666 : commandF = YES;
667 : break;
668 : }
669 :
670 : return;
671 : }
672 :
673 : keycodetrans[keycode] = key; // record the character we got for pressing the hardware at key location 'keycode'
674 :
675 : /* HACK: treat f12 as alias to cmd-F for compatibility with helpful forum
676 : advice etc.
677 : */
678 : if (key == NSF12FunctionKey)
679 : {
680 : if (!f12)
681 : {
682 : f12 = YES;
683 : [gameController performSelector:@selector(toggleFullScreenAction:) withObject:nil afterDelay:0.0];
684 : }
685 :
686 : return;
687 : };
688 :
689 : if ((key >= 0)&&(key < [self numKeys])&&(!keys[key]))
690 : {
691 : keys[key] = YES;
692 :
693 : if (allowingStringInput)
694 : {
695 : if ((key == gvDeleteKey || key == gvBackspaceKey) && [typedString length] > 0)
696 : {
697 : // delete
698 : [typedString deleteCharactersInRange:NSMakeRange([typedString length] - 1, 1)];
699 : }
700 :
701 : isAlphabetKeyDown = NO;
702 : lastKeyShifted = shift;
703 :
704 : // limited input for planet find screen
705 : if (allowingStringInput == gvStringInputAlpha)
706 : {
707 : if (isalpha(key))
708 : {
709 : isAlphabetKeyDown = YES;
710 : // convert to lower case
711 : [typedString appendFormat:@"%c", tolower(key)];
712 : }
713 : }
714 :
715 : // full input for load-save screen or 'all' input
716 : if (allowingStringInput >= gvStringInputLoadSave)
717 : {
718 : // except '/' for loadsave
719 : if (isprint(key) && key != '/')
720 : {
721 : isAlphabetKeyDown = YES;
722 : [typedString appendFormat:@"%c", key];
723 : }
724 : else if (key == '/' && allowingStringInput == gvStringInputAll)
725 : {
726 : isAlphabetKeyDown = YES;
727 : [typedString appendFormat:@"%c", key];
728 : }
729 : }
730 :
731 : }
732 : }
733 : else
734 : {
735 : if (key > [self numKeys])
736 : {
737 : OOLog(kOOLogKeyCodeOutOfRange, @"Translated key: %d out of range", key);
738 : }
739 : }
740 : }
741 :
742 : /* Capture shift, ctrl, opt and command press & release */
743 0 : - (void)flagsChanged:(NSEvent *)theEvent
744 : {
745 : NSUInteger flags = [theEvent modifierFlags];
746 : opt = (flags & NSAlternateKeyMask) ? YES : NO;
747 : ctrl = (flags & NSControlKeyMask) ? YES : NO;
748 : command = (flags & NSCommandKeyMask) ? YES : NO;
749 : shift = ( flags & NSShiftKeyMask ) ? YES : NO;
750 : if ([theEvent keyCode] == 0x39) // 57 = key code for caps lock
751 : {
752 : capsLockOn = (flags & NSAlphaShiftKeyMask) ? YES : NO;
753 : }
754 : }
755 :
756 :
757 : - (void)mouseDown:(NSEvent *)theEvent
758 : {
759 : if (doubleClick)
760 : {
761 : doubleClick = NO;
762 : keys[gvMouseDoubleClick] = NO;
763 : }
764 : keys[gvMouseLeftButton] = YES; // 'a' down
765 : }
766 :
767 :
768 : - (void)mouseUp:(NSEvent *)theEvent
769 : {
770 : NSTimeInterval timeBetweenClicks = [NSDate timeIntervalSinceReferenceDate] - timeIntervalAtLastClick;
771 : timeIntervalAtLastClick += timeBetweenClicks;
772 :
773 : if (!doubleClick)
774 : {
775 : doubleClick = (timeBetweenClicks < MOUSE_DOUBLE_CLICK_INTERVAL); // One fifth of a second
776 : keys[gvMouseDoubleClick] = doubleClick;
777 : }
778 :
779 : keys[gvMouseLeftButton] = NO; // 'a' up
780 : }
781 :
782 :
783 : - (void)resetMouse
784 : {
785 : CGPoint centerPoint = CGPointMake(viewSize.width / 2.0, viewSize.height / 2.0);
786 : CGWarpMouseCursorPosition(centerPoint);
787 : [self setVirtualJoystick:0.0 :0.0];
788 : }
789 :
790 :
791 0 : - (void)mouseMoved:(NSEvent *)theEvent
792 : {
793 : double mx = [theEvent locationInWindow].x - viewSize.width/2.0;
794 : double my = [theEvent locationInWindow].y - viewSize.height/2.0;
795 :
796 : if (display_z > 640.0)
797 : {
798 : mx /= viewSize.width * MAIN_GUI_PIXEL_WIDTH / display_z;
799 : my /= viewSize.height;
800 : }
801 : else
802 : {
803 : mx /= MAIN_GUI_PIXEL_WIDTH * viewSize.width / 640.0;
804 : my /= MAIN_GUI_PIXEL_HEIGHT * viewSize.width / 640.0;
805 : }
806 :
807 : [self setVirtualJoystick:mx :-my];
808 : }
809 :
810 :
811 0 : - (void) mouseDragged:(NSEvent *)theEvent
812 : {
813 : [self mouseMoved:theEvent];
814 : }
815 :
816 0 : - (void)scrollWheel:(NSEvent *)theEvent
817 : {
818 : float dy = [theEvent scrollingDeltaY];
819 :
820 : if (dy == 0)
821 : return;
822 :
823 : if (dy > 0)
824 : {
825 : if (_mouseWheelDelta >= 0)
826 : _mouseWheelDelta += dy;
827 : else
828 : _mouseWheelDelta = 0;
829 : }
830 : else
831 : {
832 : if (_mouseWheelDelta <= 0)
833 : _mouseWheelDelta += dy;
834 : else
835 : _mouseWheelDelta = 0;
836 : }
837 : timeIntervalAtLastMouseWheel = [NSDate timeIntervalSinceReferenceDate];
838 : }
839 :
840 0 : - (void) otherMouseDragged:(NSEvent *)theEvent
841 : {
842 : [self mouseMoved:theEvent];
843 : }
844 :
845 :
846 0 : - (void) rightMouseDown:(NSEvent *)theEvent
847 : {
848 : [self resetMouse];
849 : }
850 :
851 :
852 0 : - (void) rightMouseUp:(NSEvent *)theEvent
853 : {
854 : [self recenterVirtualJoystick];
855 : }
856 :
857 :
858 0 : - (void) touchesEndedWithEvent:(NSEvent *)theEvent
859 : {
860 : [self recenterVirtualJoystick];
861 : }
862 :
863 :
864 0 : - (void) recenterVirtualJoystick
865 : {
866 : if ([PLAYER guiScreen] == GUI_SCREEN_MAIN)
867 : {
868 : [[GameController sharedController] recenterVirtualJoystick];
869 : }
870 : }
871 :
872 :
873 : /////////////////////////////////////////////////////////////
874 : /* Turn the Cocoa ArrowKeys into our arrow key constants. */
875 0 : - (int) translateKeyCode: (int) input
876 : {
877 : int key = input;
878 : switch ( input )
879 : {
880 : case NSUpArrowFunctionKey:
881 : key = gvArrowKeyUp;
882 : break;
883 :
884 : case NSDownArrowFunctionKey:
885 : key = gvArrowKeyDown;
886 : break;
887 :
888 : case NSLeftArrowFunctionKey:
889 : key = gvArrowKeyLeft;
890 : break;
891 :
892 : case NSRightArrowFunctionKey:
893 : key = gvArrowKeyRight;
894 : break;
895 :
896 : case NSF1FunctionKey:
897 : key = gvFunctionKey1;
898 : break;
899 :
900 : case NSF2FunctionKey:
901 : key = gvFunctionKey2;
902 : break;
903 :
904 : case NSF3FunctionKey:
905 : key = gvFunctionKey3;
906 : break;
907 :
908 : case NSF4FunctionKey:
909 : key = gvFunctionKey4;
910 : break;
911 :
912 : case NSF5FunctionKey:
913 : key = gvFunctionKey5;
914 : break;
915 :
916 : case NSF6FunctionKey:
917 : key = gvFunctionKey6;
918 : break;
919 :
920 : case NSF7FunctionKey:
921 : key = gvFunctionKey7;
922 : break;
923 :
924 : case NSF8FunctionKey:
925 : key = gvFunctionKey8;
926 : break;
927 :
928 : case NSF9FunctionKey:
929 : key = gvFunctionKey9;
930 : break;
931 :
932 : case NSF10FunctionKey:
933 : key = gvFunctionKey10;
934 : break;
935 :
936 : case NSF11FunctionKey:
937 : key = gvFunctionKey11;
938 : break;
939 :
940 : case NSHomeFunctionKey:
941 : key = gvHomeKey;
942 : break;
943 :
944 : case NSBackspaceCharacter:
945 : key = gvBackspaceKey;
946 : break;
947 :
948 : case NSDeleteCharacter:
949 : key = gvDeleteKey;
950 : break;
951 :
952 : case NSInsertFunctionKey:
953 : key = gvInsertKey;
954 : break;
955 :
956 : case NSEndFunctionKey:
957 : key = gvEndKey;
958 : break;
959 :
960 : case NSPageUpFunctionKey:
961 : key = gvPageUpKey;
962 : break;
963 :
964 : case NSPageDownFunctionKey:
965 : key = gvPageDownKey;
966 : break;
967 :
968 : case NSPrintScreenFunctionKey:
969 : key = gvPrintScreenKey;
970 : break;
971 :
972 : case NSPauseFunctionKey:
973 : key = gvPauseKey;
974 : break;
975 :
976 : default:
977 : break;
978 : }
979 : return key;
980 : }
981 :
982 :
983 : - (void) setVirtualJoystick:(double) vmx :(double) vmy
984 : {
985 : virtualJoystickPosition.x = vmx;
986 : virtualJoystickPosition.y = vmy;
987 : }
988 :
989 :
990 : - (NSPoint) virtualJoystickPosition
991 : {
992 : return virtualJoystickPosition;
993 : }
994 :
995 :
996 : /////////////////////////////////////////////////////////////
997 :
998 : - (void) clearKeys
999 : {
1000 : int i;
1001 : lastKeyShifted = NO;
1002 : for (i = 0; i < [self numKeys]; i++)
1003 : keys[i] = NO;
1004 : }
1005 :
1006 :
1007 : - (void) clearMouse
1008 : {
1009 : keys[gvMouseDoubleClick] = NO;
1010 : keys[gvMouseLeftButton] = NO;
1011 : doubleClick = NO;
1012 : }
1013 :
1014 :
1015 : - (void) clearKey: (int)theKey
1016 : {
1017 : if (theKey >= 0 && theKey < [self numKeys])
1018 : {
1019 : keys[theKey] = NO;
1020 : }
1021 : }
1022 :
1023 :
1024 : - (BOOL) isAlphabetKeyDown
1025 : {
1026 : return isAlphabetKeyDown = NO;
1027 : }
1028 :
1029 : // DJS: When entering submenus in the gui, it is not helpful if the
1030 : // key down that brought you into the submenu is still registered
1031 : // as down when we're in. This makes isDown return NO until a key up
1032 : // event has been received from SDL.
1033 : - (void) suppressKeysUntilKeyUp
1034 : {
1035 : if (keys[gvMouseDoubleClick] == NO)
1036 : {
1037 : suppressKeys = YES;
1038 : [self clearKeys];
1039 : }
1040 : else
1041 : {
1042 : [self clearMouse];
1043 : }
1044 : }
1045 :
1046 :
1047 : - (BOOL) isDown: (int) key
1048 : {
1049 : if( suppressKeys )
1050 : return NO;
1051 : if ( key < 0 )
1052 : return NO;
1053 : if ( key >= [self numKeys] )
1054 : return NO;
1055 : return keys[key];
1056 : }
1057 :
1058 :
1059 : - (BOOL) isOptDown
1060 : {
1061 : return opt;
1062 : }
1063 :
1064 :
1065 : - (BOOL) isCtrlDown
1066 : {
1067 : return ctrl;
1068 : }
1069 :
1070 :
1071 : - (BOOL) isCommandDown
1072 : {
1073 : return command;
1074 : }
1075 :
1076 :
1077 : - (BOOL) isShiftDown
1078 : {
1079 : return shift;
1080 : }
1081 :
1082 :
1083 : - (BOOL) isCapsLockOn
1084 : {
1085 : return capsLockOn;
1086 : }
1087 :
1088 :
1089 : - (BOOL) lastKeyWasShifted
1090 : {
1091 : return lastKeyShifted;
1092 : }
1093 :
1094 :
1095 : - (int) numKeys
1096 : {
1097 : return NUM_KEYS;
1098 : }
1099 :
1100 :
1101 : - (int) mouseWheelState
1102 : {
1103 : if (_mouseWheelDelta > 0.0f)
1104 : return gvMouseWheelUp;
1105 : else if (_mouseWheelDelta < 0.0f)
1106 : return gvMouseWheelDown;
1107 : else
1108 : return gvMouseWheelNeutral;
1109 : }
1110 :
1111 :
1112 : - (float) mouseWheelDelta
1113 : {
1114 : return _mouseWheelDelta / OOMOUSEWHEEL_DELTA;
1115 : }
1116 :
1117 :
1118 : - (void) setMouseWheelDelta: (float) newWheelDelta
1119 : {
1120 : _mouseWheelDelta = newWheelDelta * OOMOUSEWHEEL_DELTA;
1121 : }
1122 :
1123 :
1124 : - (BOOL) isCommandQDown
1125 : {
1126 : return commandQ;
1127 : }
1128 :
1129 :
1130 : - (BOOL) isCommandFDown
1131 : {
1132 : return commandF;
1133 : }
1134 :
1135 :
1136 : - (void) clearCommandF
1137 : {
1138 : commandF = NO;
1139 : }
1140 :
1141 :
1142 : + (BOOL)pollShiftKey
1143 : {
1144 0 : #define KEYMAP_GET(m, index) ((((uint8_t*)(m))[(index) >> 3] & (1L << ((index) & 7))) ? 1 : 0)
1145 :
1146 : KeyMap map;
1147 :
1148 : GetKeys(map);
1149 : return KEYMAP_GET(map, 56) || KEYMAP_GET(map, 60); // Left shift or right shift -- although 60 shouldn't occur.
1150 : }
1151 :
1152 : - (OOOpenGLMatrixManager *) getOpenGLMatrixManager
1153 : {
1154 : return matrixManager;
1155 : }
1156 :
1157 : #ifndef NDEBUG
1158 : // General image-dumping method.
1159 : - (void) dumpRGBAToFileNamed:(NSString *)name
1160 : bytes:(uint8_t *)bytes
1161 : width:(NSUInteger)width
1162 : height:(NSUInteger)height
1163 : rowBytes:(NSUInteger)rowBytes
1164 : {
1165 : if (name == nil || bytes == NULL || width == 0 || height == 0 || rowBytes < width * 4) return;
1166 :
1167 : NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&bytes
1168 : pixelsWide:width
1169 : pixelsHigh:height
1170 : bitsPerSample:8
1171 : samplesPerPixel:4
1172 : hasAlpha:YES
1173 : isPlanar:NO
1174 : colorSpaceName:NSCalibratedRGBColorSpace
1175 : bytesPerRow:rowBytes
1176 : bitsPerPixel:32];
1177 :
1178 : if (bitmap != nil)
1179 : {
1180 : [bitmap autorelease];
1181 :
1182 : NSString *filepath = [[[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:name] stringByAppendingPathExtension:@"png"];
1183 : [[bitmap representationUsingType:NSPNGFileType properties:[NSDictionary dictionary]] writeToFile:filepath atomically:YES];
1184 : }
1185 : }
1186 :
1187 : - (void) dumpRGBToFileNamed:(NSString *)name
1188 : bytes:(uint8_t *)bytes
1189 : width:(NSUInteger)width
1190 : height:(NSUInteger)height
1191 : rowBytes:(NSUInteger)rowBytes
1192 : {
1193 : if (name == nil || bytes == NULL || width == 0 || height == 0 || rowBytes < width * 3) return;
1194 :
1195 : NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&bytes
1196 : pixelsWide:width
1197 : pixelsHigh:height
1198 : bitsPerSample:8
1199 : samplesPerPixel:3
1200 : hasAlpha:NO
1201 : isPlanar:NO
1202 : colorSpaceName:NSCalibratedRGBColorSpace
1203 : bytesPerRow:rowBytes
1204 : bitsPerPixel:24];
1205 :
1206 : if (bitmap != nil)
1207 : {
1208 : [bitmap autorelease];
1209 :
1210 : NSString *filepath = [[[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:name] stringByAppendingPathExtension:@"png"];
1211 : [[bitmap representationUsingType:NSPNGFileType properties:[NSDictionary dictionary]] writeToFile:filepath atomically:YES];
1212 : }
1213 : }
1214 :
1215 :
1216 : - (void) dumpGrayToFileNamed:(NSString *)name
1217 : bytes:(uint8_t *)bytes
1218 : width:(NSUInteger)width
1219 : height:(NSUInteger)height
1220 : rowBytes:(NSUInteger)rowBytes
1221 : {
1222 : if (name == nil || bytes == NULL || width == 0 || height == 0 || rowBytes < width) return;
1223 :
1224 : NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&bytes
1225 : pixelsWide:width
1226 : pixelsHigh:height
1227 : bitsPerSample:8
1228 : samplesPerPixel:1
1229 : hasAlpha:NO
1230 : isPlanar:NO
1231 : colorSpaceName:NSCalibratedWhiteColorSpace
1232 : bytesPerRow:rowBytes
1233 : bitsPerPixel:8];
1234 :
1235 : if (bitmap != nil)
1236 : {
1237 : [bitmap autorelease];
1238 :
1239 : NSString *filepath = [[[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:name] stringByAppendingPathExtension:@"png"];
1240 : [[bitmap representationUsingType:NSPNGFileType properties:[NSDictionary dictionary]] writeToFile:filepath atomically:YES];
1241 : }
1242 : }
1243 :
1244 :
1245 : - (void) dumpGrayAlphaToFileNamed:(NSString *)name
1246 : bytes:(uint8_t *)bytes
1247 : width:(NSUInteger)width
1248 : height:(NSUInteger)height
1249 : rowBytes:(NSUInteger)rowBytes
1250 : {
1251 : if (name == nil || bytes == NULL || width == 0 || height == 0 || rowBytes < width * 2) return;
1252 :
1253 : NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&bytes
1254 : pixelsWide:width
1255 : pixelsHigh:height
1256 : bitsPerSample:8
1257 : samplesPerPixel:2
1258 : hasAlpha:YES
1259 : isPlanar:NO
1260 : colorSpaceName:NSCalibratedWhiteColorSpace
1261 : bytesPerRow:rowBytes
1262 : bitsPerPixel:16];
1263 :
1264 : if (bitmap != nil)
1265 : {
1266 : [bitmap autorelease];
1267 :
1268 : NSString *filepath = [[[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:name] stringByAppendingPathExtension:@"png"];
1269 : [[bitmap representationUsingType:NSPNGFileType properties:[NSDictionary dictionary]] writeToFile:filepath atomically:YES];
1270 : }
1271 : }
1272 :
1273 :
1274 : - (void) dumpRGBAToRGBFileNamed:(NSString *)rgbName
1275 : andGrayFileNamed:(NSString *)grayName
1276 : bytes:(uint8_t *)bytes
1277 : width:(NSUInteger)width
1278 : height:(NSUInteger)height
1279 : rowBytes:(NSUInteger)rowBytes
1280 : {
1281 : if ((rgbName == nil && grayName == nil) || bytes == NULL || width == 0 || height == 0 || rowBytes < width * 4) return;
1282 :
1283 : uint8_t *rgbBytes, *rgbPx, *grayBytes, *grayPx, *srcPx;
1284 : NSUInteger x, y;
1285 : BOOL trivalAlpha = YES;
1286 :
1287 : rgbPx = rgbBytes = malloc(width * height * 3);
1288 : if (rgbBytes == NULL) return;
1289 :
1290 : grayPx = grayBytes = malloc(width * height);
1291 : if (grayBytes == NULL)
1292 : {
1293 : free(rgbBytes);
1294 : return;
1295 : }
1296 :
1297 : for (y = 0; y < height; y++)
1298 : {
1299 : srcPx = bytes + rowBytes * y;
1300 :
1301 : for (x = 0; x < width; x++)
1302 : {
1303 : *rgbPx++ = *srcPx++;
1304 : *rgbPx++ = *srcPx++;
1305 : *rgbPx++ = *srcPx++;
1306 : trivalAlpha = trivalAlpha && ((*srcPx == 0xFF) || (*srcPx == 0x00)); // Look for any "interesting" pixels in alpha.
1307 : *grayPx++ = *srcPx++;
1308 : }
1309 : }
1310 :
1311 : [self dumpRGBToFileNamed:rgbName
1312 : bytes:rgbBytes
1313 : width:width
1314 : height:height
1315 : rowBytes:width * 3];
1316 : free(rgbBytes);
1317 :
1318 : if (!trivalAlpha)
1319 : {
1320 : [self dumpGrayToFileNamed:grayName
1321 : bytes:grayBytes
1322 : width:width
1323 : height:height
1324 : rowBytes:width];
1325 : }
1326 : free(grayBytes);
1327 : }
1328 :
1329 : #endif
1330 :
1331 :
1332 0 : static void GetDesiredCursorState(OOMouseInteractionMode mode, BOOL *outHidden, BOOL *outObscured)
1333 : {
1334 : NSCParameterAssert(outHidden != NULL && outObscured != NULL);
1335 :
1336 : *outHidden = (mode == MOUSE_MODE_FLIGHT_WITH_MOUSE_CONTROL);
1337 : *outObscured = (mode == MOUSE_MODE_FLIGHT_NO_MOUSE_CONTROL);
1338 : }
1339 :
1340 :
1341 0 : static void ApplyCursorState(OOMouseInteractionMode mode)
1342 : {
1343 : BOOL hidden, obscured;
1344 : GetDesiredCursorState(mode, &hidden, &obscured);
1345 : if (hidden) [NSCursor hide];
1346 : if (obscured) [NSCursor setHiddenUntilMouseMoves:YES];
1347 : }
1348 :
1349 :
1350 0 : static void UnapplyCursorState(OOMouseInteractionMode mode)
1351 : {
1352 : BOOL hidden, obscured;
1353 : GetDesiredCursorState(mode, &hidden, &obscured);
1354 : if (hidden) [NSCursor unhide];
1355 : if (obscured) [NSCursor setHiddenUntilMouseMoves:NO];
1356 : }
1357 :
1358 :
1359 : - (void) setGammaValue: (float) value
1360 : {
1361 : // no-op
1362 : }
1363 :
1364 : - (float) gammaValue
1365 : {
1366 : return 1.0;
1367 : }
1368 :
1369 :
1370 : - (void) setFov:(float)value fromFraction:(BOOL)fromFraction
1371 : {
1372 : _fov = fromFraction ? value : tanf((value / 2.0f) * M_PI / 180.0f);
1373 : }
1374 :
1375 :
1376 : - (float) fov:(BOOL)inFraction
1377 : {
1378 : return inFraction ? _fov : 2 * atanf(_fov) * 180.0f / M_PI;
1379 : }
1380 :
1381 : @end
|