Oolite 1.91.0.7646-241128-10e222e
Loading...
Searching...
No Matches
MyOpenGLView.m
Go to the documentation of this file.
1/*
2
3MyOpenGLView.m
4
5Oolite
6Copyright (C) 2004-2013 Giles C Williams and contributors
7
8This program is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License
10as published by the Free Software Foundation; either version 2
11of the License, or (at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21MA 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>
36#import "PlayerEntity.h"
37
38#ifndef NDEBUG
39#import <Foundation/NSDebug.h>
40#endif
41
42
43static NSString * kOOLogKeyCodeOutOfRange = @"input.keyMapping.codeOutOfRange";
44static NSString * kOOLogKeyUp = @"input.keyMapping.keyPress.keyUp";
45static NSString * kOOLogKeyDown = @"input.keyMapping.keyPress.keyDown";
46
47
48static void GetDesiredCursorState(OOMouseInteractionMode mode, BOOL *outHidden, BOOL *outObscured);
51
52
54
55- (int) translateKeyCode:(int)input;
56
58
59@end
60
61
62#if !OOLITE_MAC_OS_X_10_7
63@interface NSView (Lion)
64
65- (BOOL) wantsBestResolutionOpenGLSurface;
66- (void) setWantsBestResolutionOpenGLSurface:(BOOL)flag;
67
68- (NSPoint) convertPointToBacking:(NSPoint)aPoint;
69- (NSPoint) convertPointFromBacking:(NSPoint)aPoint;
70- (NSSize) convertSizeToBacking:(NSSize)aSize;
71- (NSSize) convertSizeFromBacking:(NSSize)aSize;
72- (NSRect) convertRectToBacking:(NSRect)aRect;
73- (NSRect) convertRectFromBacking:(NSRect)aRect;
74
75@end
76#endif
77
78
79@implementation MyOpenGLView
80
81- (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- (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- (void) noteMovedToBadDisplay
317{
318 NSRunInformationalAlertPanel(DESC(@"oolite-mac-bad-display"), @"%@", nil, nil, nil, DESC(@"oolite-mac-bad-display-2"));
319}
320
321
322- (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 {
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
528FAIL:
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- (BOOL) acceptsFirstResponder
548{
549 return YES;
550}
551
552
553- (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- (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- (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- (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- (void) mouseDragged:(NSEvent *)theEvent
812{
813 [self mouseMoved:theEvent];
814}
815
816- (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- (void) otherMouseDragged:(NSEvent *)theEvent
841{
842 [self mouseMoved:theEvent];
843}
844
845
846- (void) rightMouseDown:(NSEvent *)theEvent
847{
848 [self resetMouse];
849}
850
851
852- (void) rightMouseUp:(NSEvent *)theEvent
853{
854 [self recenterVirtualJoystick];
855}
856
857
858- (void) touchesEndedWithEvent:(NSEvent *)theEvent
859{
860 [self recenterVirtualJoystick];
861}
862
863
864- (void) recenterVirtualJoystick
865{
866 if ([PLAYER guiScreen] == GUI_SCREEN_MAIN)
867 {
869 }
870}
871
872
874/* Turn the Cocoa ArrowKeys into our arrow key constants. */
875- (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
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 #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
1332static void GetDesiredCursorState(OOMouseInteractionMode mode, BOOL *outHidden, BOOL *outObscured)
1333{
1334 NSCParameterAssert(outHidden != NULL && outObscured != NULL);
1335
1337 *outObscured = (mode == MOUSE_MODE_FLIGHT_NO_MOUSE_CONTROL);
1338}
1339
1340
1342{
1343 BOOL hidden, obscured;
1344 GetDesiredCursorState(mode, &hidden, &obscured);
1345 if (hidden) [NSCursor hide];
1346 if (obscured) [NSCursor setHiddenUntilMouseMoves:YES];
1347}
1348
1349
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
#define MOUSE_DOUBLE_CLICK_INTERVAL
@ gvMouseWheelDown
@ gvMouseWheelNeutral
@ gvMouseWheelUp
@ gvFunctionKey2
@ gvBackspaceKey
@ gvInsertKey
@ gvFunctionKey10
@ gvFunctionKey5
@ gvDeleteKey
@ gvArrowKeyDown
@ gvFunctionKey9
@ gvFunctionKey4
@ gvEndKey
@ gvMouseDoubleClick
@ gvHomeKey
@ gvPauseKey
@ gvPrintScreenKey
@ gvFunctionKey11
@ gvFunctionKey8
@ gvPageDownKey
@ gvFunctionKey3
@ gvArrowKeyUp
@ gvArrowKeyRight
@ gvFunctionKey6
@ gvArrowKeyLeft
@ gvFunctionKey7
@ gvPageUpKey
@ gvFunctionKey1
#define OOMOUSEWHEEL_EVENTS_DELAY_INTERVAL
#define NUM_KEYS
#define OOMOUSEWHEEL_DELTA
StringInput
@ gvStringInputAlpha
@ gvStringInputNo
@ gvStringInputLoadSave
@ gvStringInputAll
static NSString * kOOLogKeyCodeOutOfRange
static void GetDesiredCursorState(OOMouseInteractionMode mode, BOOL *outHidden, BOOL *outObscured)
static NSString * kOOLogKeyDown
static NSString * kOOLogKeyUp
static void ApplyCursorState(OOMouseInteractionMode mode)
#define KEYMAP_GET(m, index)
static void UnapplyCursorState(OOMouseInteractionMode mode)
#define MAIN_GUI_PIXEL_WIDTH
#define MAIN_GUI_PIXEL_HEIGHT
#define DESTROY(x)
Definition OOCocoa.h:77
#define FAIL(s)
#define OOLog(class, format,...)
Definition OOLogging.h:88
#define M_PI
Definition OOMaths.h:73
OOMouseInteractionMode
@ MOUSE_MODE_FLIGHT_WITH_MOUSE_CONTROL
@ MOUSE_MODE_FLIGHT_NO_MOUSE_CONTROL
void GLSetDisplayScaleFactor(GLfloat factor)
Definition OOOpenGL.m:238
#define OOGL(statement)
Definition OOOpenGL.h:251
return nil
float y
float x
#define PLAYER
#define UNIVERSE
Definition Universe.h:840
#define DESC(key)
Definition Universe.h:846
GameController * sharedController()
void recenterVirtualJoystick()
NSURL * snapshotsURLCreatingIfNeeded:(BOOL create)
static void ApplyCursorState(OOMouseInteractionMode mode)
static void GetDesiredCursorState(OOMouseInteractionMode mode, BOOL *outHidden, BOOL *outObscured)
OOGraphicsResetManager * sharedManager()
NSDictionary * dictionaryFromFilesNamed:inFolder:mergeMode:cache:(NSString *fileName,[inFolder] NSString *folderName,[mergeMode] OOResourceMergeMode mergeMode,[cache] BOOL useCache)
const char * filename
Definition ioapi.h:133
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
const char int mode
Definition ioapi.h:133