mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-10 14:51:51 +00:00
Split implementation of native OS X backend into several files
This commit is contained in:
parent
40d4dc502e
commit
a67dc4148b
6 changed files with 2718 additions and 2582 deletions
|
@ -575,9 +575,11 @@ set( PLAT_COCOA_SOURCES
|
|||
posix/cocoa/hid/IOHIDElement_.c
|
||||
posix/cocoa/hid/ImmrHIDUtilAddOn.c
|
||||
posix/cocoa/critsec.cpp
|
||||
posix/cocoa/i_backend_cocoa.mm
|
||||
posix/cocoa/i_input.mm
|
||||
posix/cocoa/i_joystick.cpp
|
||||
posix/cocoa/i_timer.cpp )
|
||||
posix/cocoa/i_main.mm
|
||||
posix/cocoa/i_timer.cpp
|
||||
posix/cocoa/i_video.mm )
|
||||
|
||||
if( WIN32 )
|
||||
set( SYSTEM_SOURCES_DIR win32 )
|
||||
|
|
File diff suppressed because it is too large
Load diff
170
src/posix/cocoa/i_common.h
Normal file
170
src/posix/cocoa/i_common.h
Normal file
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
** i_common.h
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2012-2014 Alexey Lysiuk
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include <AppKit/NSApplication.h>
|
||||
|
||||
|
||||
inline bool I_IsHiDPISupported()
|
||||
{
|
||||
// The following value shoud be equal to NSAppKitVersionNumber10_7
|
||||
// and it's hard-coded in order to build on earlier SDKs
|
||||
|
||||
return NSAppKitVersionNumber >= 1138;
|
||||
}
|
||||
|
||||
void I_ProcessKeyboardEvent(NSEvent* event);
|
||||
void I_ProcessKeyboardFlagsEvent(NSEvent* event);
|
||||
|
||||
void I_ProcessMouseMoveEvent(NSEvent* event);
|
||||
void I_ProcessMouseButtonEvent(NSEvent* event);
|
||||
void I_ProcessMouseWheelEvent(NSEvent* event);
|
||||
|
||||
void I_StartupJoysticks();
|
||||
void I_ShutdownJoysticks();
|
||||
void I_ProcessJoysticks();
|
||||
|
||||
NSSize I_GetContentViewSize(const NSWindow* window);
|
||||
void I_SetMainWindowVisible(bool visible);
|
||||
void I_SetNativeMouse(bool wantNative);
|
||||
|
||||
|
||||
// The following definitions are required to build with older OS X SDKs
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050
|
||||
|
||||
typedef unsigned int NSUInteger;
|
||||
typedef int NSInteger;
|
||||
|
||||
typedef float CGFloat;
|
||||
|
||||
// From HIToolbox/Events.h
|
||||
enum
|
||||
{
|
||||
kVK_Return = 0x24,
|
||||
kVK_Tab = 0x30,
|
||||
kVK_Space = 0x31,
|
||||
kVK_Delete = 0x33,
|
||||
kVK_Escape = 0x35,
|
||||
kVK_Command = 0x37,
|
||||
kVK_Shift = 0x38,
|
||||
kVK_CapsLock = 0x39,
|
||||
kVK_Option = 0x3A,
|
||||
kVK_Control = 0x3B,
|
||||
kVK_RightShift = 0x3C,
|
||||
kVK_RightOption = 0x3D,
|
||||
kVK_RightControl = 0x3E,
|
||||
kVK_Function = 0x3F,
|
||||
kVK_F17 = 0x40,
|
||||
kVK_VolumeUp = 0x48,
|
||||
kVK_VolumeDown = 0x49,
|
||||
kVK_Mute = 0x4A,
|
||||
kVK_F18 = 0x4F,
|
||||
kVK_F19 = 0x50,
|
||||
kVK_F20 = 0x5A,
|
||||
kVK_F5 = 0x60,
|
||||
kVK_F6 = 0x61,
|
||||
kVK_F7 = 0x62,
|
||||
kVK_F3 = 0x63,
|
||||
kVK_F8 = 0x64,
|
||||
kVK_F9 = 0x65,
|
||||
kVK_F11 = 0x67,
|
||||
kVK_F13 = 0x69,
|
||||
kVK_F16 = 0x6A,
|
||||
kVK_F14 = 0x6B,
|
||||
kVK_F10 = 0x6D,
|
||||
kVK_F12 = 0x6F,
|
||||
kVK_F15 = 0x71,
|
||||
kVK_Help = 0x72,
|
||||
kVK_Home = 0x73,
|
||||
kVK_PageUp = 0x74,
|
||||
kVK_ForwardDelete = 0x75,
|
||||
kVK_F4 = 0x76,
|
||||
kVK_End = 0x77,
|
||||
kVK_F2 = 0x78,
|
||||
kVK_PageDown = 0x79,
|
||||
kVK_F1 = 0x7A,
|
||||
kVK_LeftArrow = 0x7B,
|
||||
kVK_RightArrow = 0x7C,
|
||||
kVK_DownArrow = 0x7D,
|
||||
kVK_UpArrow = 0x7E
|
||||
};
|
||||
|
||||
@interface NSView(SupportOutdatedOSX)
|
||||
- (NSPoint)convertPointFromBase:(NSPoint)aPoint;
|
||||
@end
|
||||
|
||||
@implementation NSView(SupportOutdatedOSX)
|
||||
- (NSPoint)convertPointFromBase:(NSPoint)aPoint
|
||||
{
|
||||
return [self convertPoint:aPoint fromView:nil];
|
||||
}
|
||||
@end
|
||||
|
||||
#endif // prior to 10.5
|
||||
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < 1060
|
||||
|
||||
enum
|
||||
{
|
||||
NSApplicationActivationPolicyRegular
|
||||
};
|
||||
|
||||
typedef NSInteger NSApplicationActivationPolicy;
|
||||
|
||||
@interface NSApplication(ActivationPolicy)
|
||||
- (BOOL)setActivationPolicy:(NSApplicationActivationPolicy)activationPolicy;
|
||||
@end
|
||||
|
||||
@interface NSWindow(SetStyleMask)
|
||||
- (void)setStyleMask:(NSUInteger)styleMask;
|
||||
@end
|
||||
|
||||
#endif // prior to 10.6
|
||||
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
|
||||
|
||||
@interface NSView(HiDPIStubs)
|
||||
- (NSPoint)convertPointToBacking:(NSPoint)aPoint;
|
||||
- (NSSize)convertSizeToBacking:(NSSize)aSize;
|
||||
- (NSSize)convertSizeFromBacking:(NSSize)aSize;
|
||||
|
||||
- (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag;
|
||||
@end
|
||||
|
||||
@interface NSScreen(HiDPIStubs)
|
||||
- (NSRect)convertRectToBacking:(NSRect)aRect;
|
||||
@end
|
||||
|
||||
#endif // prior to 10.7
|
692
src/posix/cocoa/i_input.mm
Normal file
692
src/posix/cocoa/i_input.mm
Normal file
|
@ -0,0 +1,692 @@
|
|||
/*
|
||||
** i_input.mm
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2012-2014 Alexey Lysiuk
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include <AppKit/AppKit.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
#include "c_console.h"
|
||||
#include "c_cvars.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "d_event.h"
|
||||
#include "d_gui.h"
|
||||
#include "dikeys.h"
|
||||
#include "doomdef.h"
|
||||
#include "doomstat.h"
|
||||
#include "v_video.h"
|
||||
|
||||
#include "i_common.h"
|
||||
#include "i_rbopts.h"
|
||||
|
||||
|
||||
EXTERN_CVAR(Int, m_use_mouse)
|
||||
|
||||
CVAR(Bool, use_mouse, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Bool, m_noprescale, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Bool, m_filter, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
||||
CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG | CVAR_ARCHIVE)
|
||||
{
|
||||
if (self < 0)
|
||||
{
|
||||
self = 0;
|
||||
}
|
||||
else if (self > 2)
|
||||
{
|
||||
self = 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern int paused, chatmodeon;
|
||||
extern constate_e ConsoleState;
|
||||
|
||||
bool GUICapture;
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// TODO: remove this magic!
|
||||
size_t s_skipMouseMoves;
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
void CheckGUICapture()
|
||||
{
|
||||
const bool wantCapture = (MENU_Off == menuactive)
|
||||
? (c_down == ConsoleState || c_falling == ConsoleState || chatmodeon)
|
||||
: (MENU_On == menuactive || MENU_OnNoPause == menuactive);
|
||||
|
||||
if (wantCapture != GUICapture)
|
||||
{
|
||||
GUICapture = wantCapture;
|
||||
|
||||
ResetButtonStates();
|
||||
}
|
||||
}
|
||||
|
||||
void CenterCursor()
|
||||
{
|
||||
NSWindow* window = [NSApp keyWindow];
|
||||
if (nil == window)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const NSRect displayRect = [[window screen] frame];
|
||||
const NSRect windowRect = [window frame];
|
||||
const CGPoint centerPoint = CGPointMake(NSMidX(windowRect), displayRect.size.height - NSMidY(windowRect));
|
||||
|
||||
CGEventSourceRef eventSource = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
|
||||
|
||||
if (NULL != eventSource)
|
||||
{
|
||||
CGEventRef mouseMoveEvent = CGEventCreateMouseEvent(eventSource,
|
||||
kCGEventMouseMoved, centerPoint, kCGMouseButtonLeft);
|
||||
|
||||
if (NULL != mouseMoveEvent)
|
||||
{
|
||||
CGEventPost(kCGHIDEventTap, mouseMoveEvent);
|
||||
CFRelease(mouseMoveEvent);
|
||||
}
|
||||
|
||||
CFRelease(eventSource);
|
||||
}
|
||||
|
||||
// TODO: remove this magic!
|
||||
s_skipMouseMoves = 2;
|
||||
}
|
||||
|
||||
|
||||
bool IsInGame()
|
||||
{
|
||||
switch (mouse_capturemode)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
return gamestate == GS_LEVEL;
|
||||
|
||||
case 1:
|
||||
return gamestate == GS_LEVEL
|
||||
|| gamestate == GS_INTERMISSION
|
||||
|| gamestate == GS_FINALE;
|
||||
|
||||
case 2:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckNativeMouse()
|
||||
{
|
||||
const bool windowed = (NULL == screen) || !screen->IsFullscreen();
|
||||
bool wantNative;
|
||||
|
||||
if (windowed)
|
||||
{
|
||||
if (![NSApp isActive] || !use_mouse)
|
||||
{
|
||||
wantNative = true;
|
||||
}
|
||||
else if (MENU_WaitKey == menuactive)
|
||||
{
|
||||
wantNative = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
wantNative = (!m_use_mouse || MENU_WaitKey != menuactive)
|
||||
&& (!IsInGame() || GUICapture || paused || demoplayback);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ungrab mouse when in the menu with mouse control on.
|
||||
wantNative = m_use_mouse
|
||||
&& (MENU_On == menuactive || MENU_OnNoPause == menuactive);
|
||||
}
|
||||
|
||||
I_SetNativeMouse(wantNative);
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
|
||||
void I_GetEvent()
|
||||
{
|
||||
[[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
void I_StartTic()
|
||||
{
|
||||
CheckGUICapture();
|
||||
CheckNativeMouse();
|
||||
|
||||
I_ProcessJoysticks();
|
||||
I_GetEvent();
|
||||
}
|
||||
|
||||
void I_StartFrame()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void I_SetMouseCapture()
|
||||
{
|
||||
}
|
||||
|
||||
void I_ReleaseMouseCapture()
|
||||
{
|
||||
}
|
||||
|
||||
void I_SetNativeMouse(bool wantNative)
|
||||
{
|
||||
static bool nativeMouse = true;
|
||||
|
||||
if (wantNative != nativeMouse)
|
||||
{
|
||||
nativeMouse = wantNative;
|
||||
|
||||
if (!wantNative)
|
||||
{
|
||||
CenterCursor();
|
||||
}
|
||||
|
||||
CGAssociateMouseAndMouseCursorPosition(wantNative);
|
||||
|
||||
if (wantNative)
|
||||
{
|
||||
[NSCursor unhide];
|
||||
}
|
||||
else
|
||||
{
|
||||
[NSCursor hide];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const size_t KEY_COUNT = 128;
|
||||
|
||||
|
||||
// See Carbon -> HIToolbox -> Events.h for kVK_ constants
|
||||
|
||||
const uint8_t KEYCODE_TO_DIK[KEY_COUNT] =
|
||||
{
|
||||
DIK_A, DIK_S, DIK_D, DIK_F, DIK_H, DIK_G, DIK_Z, DIK_X, // 0x00 - 0x07
|
||||
DIK_C, DIK_V, 0, DIK_B, DIK_Q, DIK_W, DIK_E, DIK_R, // 0x08 - 0x0F
|
||||
DIK_Y, DIK_T, DIK_1, DIK_2, DIK_3, DIK_4, DIK_6, DIK_5, // 0x10 - 0x17
|
||||
DIK_EQUALS, DIK_9, DIK_7, DIK_MINUS, DIK_8, DIK_0, DIK_RBRACKET, DIK_O, // 0x18 - 0x1F
|
||||
DIK_U, DIK_LBRACKET, DIK_I, DIK_P, DIK_RETURN, DIK_L, DIK_J, DIK_APOSTROPHE, // 0x20 - 0x27
|
||||
DIK_K, DIK_SEMICOLON, DIK_BACKSLASH, DIK_COMMA, DIK_SLASH, DIK_N, DIK_M, DIK_PERIOD, // 0x28 - 0x2F
|
||||
DIK_TAB, DIK_SPACE, DIK_GRAVE, DIK_BACK, 0, DIK_ESCAPE, 0, DIK_LWIN, // 0x30 - 0x37
|
||||
DIK_LSHIFT, DIK_CAPITAL, DIK_LMENU, DIK_LCONTROL, DIK_RSHIFT, DIK_RMENU, DIK_RCONTROL, 0, // 0x38 - 0x3F
|
||||
0, DIK_DECIMAL, 0, DIK_MULTIPLY, 0, DIK_ADD, 0, 0, // 0x40 - 0x47
|
||||
DIK_VOLUMEUP, DIK_VOLUMEDOWN, DIK_MUTE, DIK_SLASH, DIK_NUMPADENTER, 0, DIK_SUBTRACT, 0, // 0x48 - 0x4F
|
||||
0, DIK_NUMPAD_EQUALS, DIK_NUMPAD0, DIK_NUMPAD1, DIK_NUMPAD2, DIK_NUMPAD3, DIK_NUMPAD4, DIK_NUMPAD5, // 0x50 - 0x57
|
||||
DIK_NUMPAD6, DIK_NUMPAD7, 0, DIK_NUMPAD8, DIK_NUMPAD9, 0, 0, 0, // 0x58 - 0x5F
|
||||
DIK_F5, DIK_F6, DIK_F7, DIK_F3, DIK_F8, DIK_F9, 0, DIK_F11, // 0x60 - 0x67
|
||||
0, DIK_F13, 0, DIK_F14, 0, DIK_F10, 0, DIK_F12, // 0x68 - 0x6F
|
||||
0, DIK_F15, 0, DIK_HOME, 0, DIK_DELETE, DIK_F4, DIK_END, // 0x70 - 0x77
|
||||
DIK_F2, 0, DIK_F1, DIK_LEFT, DIK_RIGHT, DIK_DOWN, DIK_UP, 0, // 0x78 - 0x7F
|
||||
};
|
||||
|
||||
const uint8_t KEYCODE_TO_ASCII[KEY_COUNT] =
|
||||
{
|
||||
'a', 's', 'd', 'f', 'h', 'g', 'z', 'x', // 0x00 - 0x07
|
||||
'c', 'v', 0, 'b', 'q', 'w', 'e', 'r', // 0x08 - 0x0F
|
||||
'y', 't', '1', '2', '3', '4', '6', '5', // 0x10 - 0x17
|
||||
'=', '9', '7', '-', '8', '0', ']', 'o', // 0x18 - 0x1F
|
||||
'u', '[', 'i', 'p', 13, 'l', 'j', '\'', // 0x20 - 0x27
|
||||
'k', ';', '\\', ',', '/', 'n', 'm', '.', // 0x28 - 0x2F
|
||||
9, ' ', '`', 12, 0, 27, 0, 0, // 0x30 - 0x37
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // 0x38 - 0x3F
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // 0x40 - 0x47
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // 0x48 - 0x4F
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // 0x50 - 0x57
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // 0x58 - 0x5F
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // 0x60 - 0x67
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // 0x68 - 0x6F
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // 0x70 - 0x77
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // 0x78 - 0x7F
|
||||
};
|
||||
|
||||
|
||||
uint8_t ModifierToDIK(const uint32_t modifier)
|
||||
{
|
||||
switch (modifier)
|
||||
{
|
||||
case NSAlphaShiftKeyMask: return DIK_CAPITAL;
|
||||
case NSShiftKeyMask: return DIK_LSHIFT;
|
||||
case NSControlKeyMask: return DIK_LCONTROL;
|
||||
case NSAlternateKeyMask: return DIK_LMENU;
|
||||
case NSCommandKeyMask: return DIK_LWIN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SWORD ModifierFlagsToGUIKeyModifiers(NSEvent* theEvent)
|
||||
{
|
||||
const NSUInteger modifiers([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask);
|
||||
return ((modifiers & NSShiftKeyMask ) ? GKM_SHIFT : 0)
|
||||
| ((modifiers & NSControlKeyMask ) ? GKM_CTRL : 0)
|
||||
| ((modifiers & NSAlternateKeyMask) ? GKM_ALT : 0)
|
||||
| ((modifiers & NSCommandKeyMask ) ? GKM_META : 0);
|
||||
}
|
||||
|
||||
bool ShouldGenerateGUICharEvent(NSEvent* theEvent)
|
||||
{
|
||||
const NSUInteger modifiers([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask);
|
||||
return !(modifiers & NSControlKeyMask)
|
||||
&& !(modifiers & NSAlternateKeyMask)
|
||||
&& !(modifiers & NSCommandKeyMask)
|
||||
&& !(modifiers & NSFunctionKeyMask);
|
||||
}
|
||||
|
||||
|
||||
NSStringEncoding GetEncodingForUnicodeCharacter(const unichar character)
|
||||
{
|
||||
if (character >= L'\u0100' && character <= L'\u024F')
|
||||
{
|
||||
return NSWindowsCP1250StringEncoding; // Central and Eastern Europe
|
||||
}
|
||||
else if (character >= L'\u0370' && character <= L'\u03FF')
|
||||
{
|
||||
return NSWindowsCP1253StringEncoding; // Greek
|
||||
}
|
||||
else if (character >= L'\u0400' && character <= L'\u04FF')
|
||||
{
|
||||
return NSWindowsCP1251StringEncoding; // Cyrillic
|
||||
}
|
||||
|
||||
// TODO: add handling for other characters
|
||||
// TODO: Turkish should use NSWindowsCP1254StringEncoding
|
||||
|
||||
return NSWindowsCP1252StringEncoding;
|
||||
}
|
||||
|
||||
unsigned char GetCharacterFromNSEvent(NSEvent* theEvent)
|
||||
{
|
||||
const NSString* unicodeCharacters = [theEvent characters];
|
||||
|
||||
if (0 == [unicodeCharacters length])
|
||||
{
|
||||
return '\0';
|
||||
}
|
||||
|
||||
const unichar unicodeCharacter = [unicodeCharacters characterAtIndex:0];
|
||||
const NSStringEncoding encoding = GetEncodingForUnicodeCharacter(unicodeCharacter);
|
||||
|
||||
unsigned char character = '\0';
|
||||
|
||||
if (NSWindowsCP1252StringEncoding == encoding)
|
||||
{
|
||||
// TODO: make sure that the following is always correct
|
||||
character = unicodeCharacter & 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
const NSData* const characters =
|
||||
[[theEvent characters] dataUsingEncoding:encoding];
|
||||
|
||||
character = [characters length] > 0
|
||||
? *static_cast<const unsigned char*>([characters bytes])
|
||||
: '\0';
|
||||
}
|
||||
|
||||
return character;
|
||||
}
|
||||
|
||||
void ProcessKeyboardEventInMenu(NSEvent* theEvent)
|
||||
{
|
||||
event_t event = {};
|
||||
|
||||
event.type = EV_GUI_Event;
|
||||
event.subtype = NSKeyDown == [theEvent type] ? EV_GUI_KeyDown : EV_GUI_KeyUp;
|
||||
event.data2 = GetCharacterFromNSEvent(theEvent);
|
||||
event.data3 = ModifierFlagsToGUIKeyModifiers(theEvent);
|
||||
|
||||
if (EV_GUI_KeyDown == event.subtype && [theEvent isARepeat])
|
||||
{
|
||||
event.subtype = EV_GUI_KeyRepeat;
|
||||
}
|
||||
|
||||
const unsigned short keyCode = [theEvent keyCode];
|
||||
|
||||
switch (keyCode)
|
||||
{
|
||||
case kVK_Return: event.data1 = GK_RETURN; break;
|
||||
case kVK_PageUp: event.data1 = GK_PGUP; break;
|
||||
case kVK_PageDown: event.data1 = GK_PGDN; break;
|
||||
case kVK_End: event.data1 = GK_END; break;
|
||||
case kVK_Home: event.data1 = GK_HOME; break;
|
||||
case kVK_LeftArrow: event.data1 = GK_LEFT; break;
|
||||
case kVK_RightArrow: event.data1 = GK_RIGHT; break;
|
||||
case kVK_UpArrow: event.data1 = GK_UP; break;
|
||||
case kVK_DownArrow: event.data1 = GK_DOWN; break;
|
||||
case kVK_Delete: event.data1 = GK_BACKSPACE; break;
|
||||
case kVK_ForwardDelete: event.data1 = GK_DEL; break;
|
||||
case kVK_Escape: event.data1 = GK_ESCAPE; break;
|
||||
case kVK_F1: event.data1 = GK_F1; break;
|
||||
case kVK_F2: event.data1 = GK_F2; break;
|
||||
case kVK_F3: event.data1 = GK_F3; break;
|
||||
case kVK_F4: event.data1 = GK_F4; break;
|
||||
case kVK_F5: event.data1 = GK_F5; break;
|
||||
case kVK_F6: event.data1 = GK_F6; break;
|
||||
case kVK_F7: event.data1 = GK_F7; break;
|
||||
case kVK_F8: event.data1 = GK_F8; break;
|
||||
case kVK_F9: event.data1 = GK_F9; break;
|
||||
case kVK_F10: event.data1 = GK_F10; break;
|
||||
case kVK_F11: event.data1 = GK_F11; break;
|
||||
case kVK_F12: event.data1 = GK_F12; break;
|
||||
default:
|
||||
event.data1 = KEYCODE_TO_ASCII[keyCode];
|
||||
break;
|
||||
}
|
||||
|
||||
if (event.data1 < 128)
|
||||
{
|
||||
event.data1 = toupper(event.data1);
|
||||
|
||||
D_PostEvent(&event);
|
||||
}
|
||||
|
||||
if (!iscntrl(event.data2)
|
||||
&& EV_GUI_KeyUp != event.subtype
|
||||
&& ShouldGenerateGUICharEvent(theEvent))
|
||||
{
|
||||
event.subtype = EV_GUI_Char;
|
||||
event.data1 = event.data2;
|
||||
event.data2 = event.data3 & GKM_ALT;
|
||||
|
||||
D_PostEvent(&event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NSEventToGameMousePosition(NSEvent* inEvent, event_t* outEvent)
|
||||
{
|
||||
const NSWindow* window = [inEvent window];
|
||||
const NSView* view = [window contentView];
|
||||
|
||||
const NSPoint screenPos = [NSEvent mouseLocation];
|
||||
const NSPoint windowPos = [window convertScreenToBase:screenPos];
|
||||
|
||||
const NSPoint viewPos = I_IsHiDPISupported()
|
||||
? [view convertPointToBacking:windowPos]
|
||||
: [view convertPoint:windowPos fromView:nil];
|
||||
|
||||
const CGFloat frameHeight = I_GetContentViewSize(window).height;
|
||||
|
||||
const CGFloat posX = ( viewPos.x - rbOpts.shiftX) / rbOpts.pixelScale;
|
||||
const CGFloat posY = (frameHeight - viewPos.y - rbOpts.shiftY) / rbOpts.pixelScale;
|
||||
|
||||
outEvent->data1 = static_cast<int>(posX);
|
||||
outEvent->data2 = static_cast<int>(posY);
|
||||
}
|
||||
|
||||
void ProcessMouseMoveInMenu(NSEvent* theEvent)
|
||||
{
|
||||
event_t event = {};
|
||||
|
||||
event.type = EV_GUI_Event;
|
||||
event.subtype = EV_GUI_MouseMove;
|
||||
|
||||
NSEventToGameMousePosition(theEvent, &event);
|
||||
|
||||
D_PostEvent(&event);
|
||||
}
|
||||
|
||||
void ProcessMouseMoveInGame(NSEvent* theEvent)
|
||||
{
|
||||
if (!use_mouse)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: remove this magic!
|
||||
|
||||
if (s_skipMouseMoves > 0)
|
||||
{
|
||||
--s_skipMouseMoves;
|
||||
return;
|
||||
}
|
||||
|
||||
int x([theEvent deltaX]);
|
||||
int y(-[theEvent deltaY]);
|
||||
|
||||
if (0 == x && 0 == y)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_noprescale)
|
||||
{
|
||||
x *= 3;
|
||||
y *= 2;
|
||||
}
|
||||
|
||||
event_t event = {};
|
||||
|
||||
static int lastX = 0, lastY = 0;
|
||||
|
||||
if (m_filter)
|
||||
{
|
||||
event.x = (x + lastX) / 2;
|
||||
event.y = (y + lastY) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
}
|
||||
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
|
||||
if (0 != event.x | 0 != event.y)
|
||||
{
|
||||
event.type = EV_Mouse;
|
||||
|
||||
D_PostEvent(&event);
|
||||
}
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
|
||||
void I_ProcessKeyboardEvent(NSEvent* theEvent)
|
||||
{
|
||||
const unsigned short keyCode = [theEvent keyCode];
|
||||
if (keyCode >= KEY_COUNT)
|
||||
{
|
||||
assert(!"Unknown keycode");
|
||||
return;
|
||||
}
|
||||
|
||||
if (GUICapture)
|
||||
{
|
||||
ProcessKeyboardEventInMenu(theEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
event_t event = {};
|
||||
|
||||
event.type = NSKeyDown == [theEvent type] ? EV_KeyDown : EV_KeyUp;
|
||||
event.data1 = KEYCODE_TO_DIK[ keyCode ];
|
||||
|
||||
if (0 != event.data1)
|
||||
{
|
||||
event.data2 = KEYCODE_TO_ASCII[ keyCode ];
|
||||
|
||||
D_PostEvent(&event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void I_ProcessKeyboardFlagsEvent(NSEvent* theEvent)
|
||||
{
|
||||
static const uint32_t FLAGS_MASK =
|
||||
NSDeviceIndependentModifierFlagsMask & ~NSNumericPadKeyMask;
|
||||
|
||||
const uint32_t modifiers = [theEvent modifierFlags] & FLAGS_MASK;
|
||||
static uint32_t oldModifiers = 0;
|
||||
const uint32_t deltaModifiers = modifiers ^ oldModifiers;
|
||||
|
||||
if (0 == deltaModifiers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
event_t event = {};
|
||||
|
||||
event.type = modifiers > oldModifiers ? EV_KeyDown : EV_KeyUp;
|
||||
event.data1 = ModifierToDIK(deltaModifiers);
|
||||
|
||||
oldModifiers = modifiers;
|
||||
|
||||
// Caps Lock is a modifier key which generates one event per state change
|
||||
// but not per actual key press or release. So treat any event as key down
|
||||
// Also its event should be not be posted in menu and console
|
||||
|
||||
if (DIK_CAPITAL == event.data1)
|
||||
{
|
||||
if (GUICapture)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
event.type = EV_KeyDown;
|
||||
}
|
||||
|
||||
D_PostEvent(&event);
|
||||
}
|
||||
|
||||
|
||||
void I_ProcessMouseMoveEvent(NSEvent* theEvent)
|
||||
{
|
||||
if (GUICapture)
|
||||
{
|
||||
ProcessMouseMoveInMenu(theEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessMouseMoveInGame(theEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void I_ProcessMouseButtonEvent(NSEvent* theEvent)
|
||||
{
|
||||
event_t event = {};
|
||||
|
||||
const NSEventType cocoaEventType = [theEvent type];
|
||||
|
||||
if (GUICapture)
|
||||
{
|
||||
event.type = EV_GUI_Event;
|
||||
|
||||
switch (cocoaEventType)
|
||||
{
|
||||
case NSLeftMouseDown: event.subtype = EV_GUI_LButtonDown; break;
|
||||
case NSRightMouseDown: event.subtype = EV_GUI_RButtonDown; break;
|
||||
case NSOtherMouseDown: event.subtype = EV_GUI_MButtonDown; break;
|
||||
case NSLeftMouseUp: event.subtype = EV_GUI_LButtonUp; break;
|
||||
case NSRightMouseUp: event.subtype = EV_GUI_RButtonUp; break;
|
||||
case NSOtherMouseUp: event.subtype = EV_GUI_MButtonUp; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
NSEventToGameMousePosition(theEvent, &event);
|
||||
|
||||
D_PostEvent(&event);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (cocoaEventType)
|
||||
{
|
||||
case NSLeftMouseDown:
|
||||
case NSRightMouseDown:
|
||||
case NSOtherMouseDown:
|
||||
event.type = EV_KeyDown;
|
||||
break;
|
||||
|
||||
case NSLeftMouseUp:
|
||||
case NSRightMouseUp:
|
||||
case NSOtherMouseUp:
|
||||
event.type = EV_KeyUp;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
event.data1 = MIN(KEY_MOUSE1 + [theEvent buttonNumber], NSInteger(KEY_MOUSE8));
|
||||
|
||||
D_PostEvent(&event);
|
||||
}
|
||||
}
|
||||
|
||||
void I_ProcessMouseWheelEvent(NSEvent* theEvent)
|
||||
{
|
||||
const CGFloat delta = [theEvent deltaY];
|
||||
const bool isZeroDelta = fabs(delta) < 1.0E-5;
|
||||
|
||||
if (isZeroDelta && GUICapture)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
event_t event = {};
|
||||
|
||||
if (GUICapture)
|
||||
{
|
||||
event.type = EV_GUI_Event;
|
||||
event.subtype = delta > 0.0f ? EV_GUI_WheelUp : EV_GUI_WheelDown;
|
||||
event.data3 = delta;
|
||||
event.data3 = ModifierFlagsToGUIKeyModifiers(theEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
event.type = isZeroDelta ? EV_KeyUp : EV_KeyDown;
|
||||
event.data1 = delta > 0.0f ? KEY_MWHEELUP : KEY_MWHEELDOWN;
|
||||
}
|
||||
|
||||
D_PostEvent(&event);
|
||||
}
|
626
src/posix/cocoa/i_main.mm
Normal file
626
src/posix/cocoa/i_main.mm
Normal file
|
@ -0,0 +1,626 @@
|
|||
/*
|
||||
** i_main.mm
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2012-2014 Alexey Lysiuk
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <AppKit/NSMenu.h>
|
||||
#include <AppKit/NSScreen.h>
|
||||
|
||||
#include "c_console.h"
|
||||
#include "c_cvars.h"
|
||||
#include "cmdlib.h"
|
||||
#include "d_main.h"
|
||||
#include "doomerrors.h"
|
||||
#include "i_system.h"
|
||||
#include "m_argv.h"
|
||||
#include "s_sound.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "i_common.h"
|
||||
#include "i_osversion.h"
|
||||
#include "i_rbopts.h"
|
||||
|
||||
|
||||
#define ZD_UNUSED(VARIABLE) ((void)(VARIABLE))
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
EXTERN_CVAR(Int, vid_defwidth )
|
||||
EXTERN_CVAR(Int, vid_defheight)
|
||||
EXTERN_CVAR(Bool, vid_vsync )
|
||||
EXTERN_CVAR(Bool, fullscreen )
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
DArgs* Args; // command line arguments
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// The maximum number of functions that can be registered with atterm.
|
||||
static const size_t MAX_TERMS = 64;
|
||||
|
||||
static void (*TermFuncs[MAX_TERMS])();
|
||||
static const char *TermNames[MAX_TERMS];
|
||||
static size_t NumTerms;
|
||||
|
||||
void call_terms()
|
||||
{
|
||||
while (NumTerms > 0)
|
||||
{
|
||||
TermFuncs[--NumTerms]();
|
||||
}
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
|
||||
void addterm(void (*func)(), const char *name)
|
||||
{
|
||||
// Make sure this function wasn't already registered.
|
||||
|
||||
for (size_t i = 0; i < NumTerms; ++i)
|
||||
{
|
||||
if (TermFuncs[i] == func)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (NumTerms == MAX_TERMS)
|
||||
{
|
||||
func();
|
||||
I_FatalError("Too many exit functions registered.");
|
||||
}
|
||||
|
||||
TermNames[NumTerms] = name;
|
||||
TermFuncs[NumTerms] = func;
|
||||
|
||||
++NumTerms;
|
||||
}
|
||||
|
||||
void popterm()
|
||||
{
|
||||
if (NumTerms)
|
||||
{
|
||||
--NumTerms;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Mac_I_FatalError(const char* const message)
|
||||
{
|
||||
I_SetMainWindowVisible(false);
|
||||
|
||||
const CFStringRef errorString = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault,
|
||||
message, kCFStringEncodingASCII, kCFAllocatorNull);
|
||||
|
||||
if (NULL != errorString)
|
||||
{
|
||||
CFOptionFlags dummy;
|
||||
|
||||
CFUserNotificationDisplayAlert( 0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL,
|
||||
CFSTR("Fatal Error"), errorString, CFSTR("Exit"), NULL, NULL, &dummy);
|
||||
|
||||
CFRelease(errorString);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const int ARGC_MAX = 64;
|
||||
|
||||
int s_argc;
|
||||
char* s_argv[ARGC_MAX];
|
||||
|
||||
TArray<FString> s_argvStorage;
|
||||
|
||||
bool s_restartedFromWADPicker;
|
||||
|
||||
|
||||
void NewFailure()
|
||||
{
|
||||
I_FatalError("Failed to allocate memory from system heap");
|
||||
}
|
||||
|
||||
|
||||
int OriginalMain(int argc, char** argv)
|
||||
{
|
||||
printf(GAMENAME" %s - %s - Cocoa version\nCompiled on %s\n\n",
|
||||
GetVersionString(), GetGitTime(), __DATE__);
|
||||
|
||||
seteuid(getuid());
|
||||
std::set_new_handler(NewFailure);
|
||||
|
||||
// Set LC_NUMERIC environment variable in case some library decides to
|
||||
// clear the setlocale call at least this will be correct.
|
||||
// Note that the LANG environment variable is overridden by LC_*
|
||||
setenv("LC_NUMERIC", "C", 1);
|
||||
setlocale(LC_ALL, "C");
|
||||
|
||||
// Set reasonable default values for video settings
|
||||
|
||||
const NSSize screenSize = [[NSScreen mainScreen] frame].size;
|
||||
vid_defwidth = static_cast<int>(screenSize.width);
|
||||
vid_defheight = static_cast<int>(screenSize.height);
|
||||
vid_vsync = true;
|
||||
fullscreen = true;
|
||||
|
||||
try
|
||||
{
|
||||
Args = new DArgs(argc, argv);
|
||||
|
||||
/*
|
||||
killough 1/98:
|
||||
|
||||
This fixes some problems with exit handling
|
||||
during abnormal situations.
|
||||
|
||||
The old code called I_Quit() to end program,
|
||||
while now I_Quit() is installed as an exit
|
||||
handler and exit() is called to exit, either
|
||||
normally or abnormally. Seg faults are caught
|
||||
and the error handler is used, to prevent
|
||||
being left in graphics mode or having very
|
||||
loud SFX noise because the sound card is
|
||||
left in an unstable state.
|
||||
*/
|
||||
|
||||
atexit (call_terms);
|
||||
atterm (I_Quit);
|
||||
|
||||
// Should we even be doing anything with progdir on Unix systems?
|
||||
char program[PATH_MAX];
|
||||
if (realpath (argv[0], program) == NULL)
|
||||
strcpy (program, argv[0]);
|
||||
char *slash = strrchr (program, '/');
|
||||
if (slash != NULL)
|
||||
{
|
||||
*(slash + 1) = '\0';
|
||||
progdir = program;
|
||||
}
|
||||
else
|
||||
{
|
||||
progdir = "./";
|
||||
}
|
||||
|
||||
I_StartupJoysticks();
|
||||
atterm(I_ShutdownJoysticks);
|
||||
|
||||
C_InitConsole(80 * 8, 25 * 8, false);
|
||||
D_DoomMain();
|
||||
}
|
||||
catch(const CDoomError& error)
|
||||
{
|
||||
const char* const message = error.GetMessage();
|
||||
|
||||
if (NULL != message)
|
||||
{
|
||||
fprintf(stderr, "%s\n", message);
|
||||
Mac_I_FatalError(message);
|
||||
}
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
call_terms();
|
||||
throw;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@interface ApplicationController : NSResponder
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
|
||||
<NSFileManagerDelegate>
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
- (void)keyDown:(NSEvent*)theEvent;
|
||||
- (void)keyUp:(NSEvent*)theEvent;
|
||||
|
||||
- (void)applicationDidBecomeActive:(NSNotification*)aNotification;
|
||||
- (void)applicationWillResignActive:(NSNotification*)aNotification;
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification;
|
||||
|
||||
- (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename;
|
||||
|
||||
- (void)processEvents:(NSTimer*)timer;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
ApplicationController* appCtrl;
|
||||
|
||||
|
||||
@implementation ApplicationController
|
||||
|
||||
- (void)keyDown:(NSEvent*)theEvent
|
||||
{
|
||||
// Empty but present to avoid playing of 'beep' alert sound
|
||||
|
||||
ZD_UNUSED(theEvent);
|
||||
}
|
||||
|
||||
- (void)keyUp:(NSEvent*)theEvent
|
||||
{
|
||||
// Empty but present to avoid playing of 'beep' alert sound
|
||||
|
||||
ZD_UNUSED(theEvent);
|
||||
}
|
||||
|
||||
|
||||
- (void)applicationDidBecomeActive:(NSNotification*)aNotification
|
||||
{
|
||||
ZD_UNUSED(aNotification);
|
||||
|
||||
S_SetSoundPaused(1);
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(NSNotification*)aNotification
|
||||
{
|
||||
ZD_UNUSED(aNotification);
|
||||
|
||||
S_SetSoundPaused(0);
|
||||
}
|
||||
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification
|
||||
{
|
||||
// When starting from command line with real executable path, e.g. ZDoom.app/Contents/MacOS/ZDoom
|
||||
// application remains deactivated for an unknown reason.
|
||||
// The following call resolves this issue
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
|
||||
// Setup timer for custom event loop
|
||||
|
||||
NSTimer* timer = [NSTimer timerWithTimeInterval:0
|
||||
target:self
|
||||
selector:@selector(processEvents:)
|
||||
userInfo:nil
|
||||
repeats:YES];
|
||||
[[NSRunLoop currentRunLoop] addTimer:timer
|
||||
forMode:NSDefaultRunLoopMode];
|
||||
|
||||
exit(OriginalMain(s_argc, s_argv));
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename
|
||||
{
|
||||
ZD_UNUSED(theApplication);
|
||||
|
||||
if (s_restartedFromWADPicker
|
||||
|| 0 == [filename length]
|
||||
|| s_argc + 2 >= ARGC_MAX)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Some parameters from command line are passed to this function
|
||||
// These parameters need to be skipped to avoid duplication
|
||||
// Note: SDL has different approach to fix this issue, see the same method in SDLMain.m
|
||||
|
||||
const char* const charFileName = [filename UTF8String];
|
||||
|
||||
for (int i = 0; i < s_argc; ++i)
|
||||
{
|
||||
if (0 == strcmp(s_argv[i], charFileName))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
s_argvStorage.Push("-file");
|
||||
s_argv[s_argc++] = s_argvStorage.Last().LockBuffer();
|
||||
|
||||
s_argvStorage.Push([filename UTF8String]);
|
||||
s_argv[s_argc++] = s_argvStorage.Last().LockBuffer();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
- (void)processEvents:(NSTimer*)timer
|
||||
{
|
||||
ZD_UNUSED(timer);
|
||||
|
||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
while (true)
|
||||
{
|
||||
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
|
||||
untilDate:[NSDate dateWithTimeIntervalSinceNow:0]
|
||||
inMode:NSDefaultRunLoopMode
|
||||
dequeue:YES];
|
||||
if (nil == event)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const NSEventType eventType = [event type];
|
||||
|
||||
switch (eventType)
|
||||
{
|
||||
case NSMouseMoved:
|
||||
I_ProcessMouseMoveEvent(event);
|
||||
break;
|
||||
|
||||
case NSLeftMouseDown:
|
||||
case NSLeftMouseUp:
|
||||
case NSRightMouseDown:
|
||||
case NSRightMouseUp:
|
||||
case NSOtherMouseDown:
|
||||
case NSOtherMouseUp:
|
||||
I_ProcessMouseButtonEvent(event);
|
||||
break;
|
||||
|
||||
case NSLeftMouseDragged:
|
||||
case NSRightMouseDragged:
|
||||
case NSOtherMouseDragged:
|
||||
I_ProcessMouseButtonEvent(event);
|
||||
I_ProcessMouseMoveEvent(event);
|
||||
break;
|
||||
|
||||
case NSScrollWheel:
|
||||
I_ProcessMouseWheelEvent(event);
|
||||
break;
|
||||
|
||||
case NSKeyDown:
|
||||
case NSKeyUp:
|
||||
I_ProcessKeyboardEvent(event);
|
||||
break;
|
||||
|
||||
case NSFlagsChanged:
|
||||
I_ProcessKeyboardFlagsEvent(event);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
[NSApp sendEvent:event];
|
||||
}
|
||||
|
||||
[NSApp updateWindows];
|
||||
|
||||
[pool release];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
NSMenuItem* CreateApplicationMenu()
|
||||
{
|
||||
NSMenu* menu = [NSMenu new];
|
||||
|
||||
[menu addItemWithTitle:[@"About " stringByAppendingString:@GAMENAME]
|
||||
action:@selector(orderFrontStandardAboutPanel:)
|
||||
keyEquivalent:@""];
|
||||
[menu addItem:[NSMenuItem separatorItem]];
|
||||
[menu addItemWithTitle:[@"Hide " stringByAppendingString:@GAMENAME]
|
||||
action:@selector(hide:)
|
||||
keyEquivalent:@"h"];
|
||||
[[menu addItemWithTitle:@"Hide Others"
|
||||
action:@selector(hideOtherApplications:)
|
||||
keyEquivalent:@"h"]
|
||||
setKeyEquivalentModifierMask:NSAlternateKeyMask | NSCommandKeyMask];
|
||||
[menu addItemWithTitle:@"Show All"
|
||||
action:@selector(unhideAllApplications:)
|
||||
keyEquivalent:@""];
|
||||
[menu addItem:[NSMenuItem separatorItem]];
|
||||
[menu addItemWithTitle:[@"Quit " stringByAppendingString:@GAMENAME]
|
||||
action:@selector(terminate:)
|
||||
keyEquivalent:@"q"];
|
||||
|
||||
NSMenuItem* menuItem = [NSMenuItem new];
|
||||
[menuItem setSubmenu:menu];
|
||||
|
||||
if ([NSApp respondsToSelector:@selector(setAppleMenu:)])
|
||||
{
|
||||
[NSApp performSelector:@selector(setAppleMenu:) withObject:menu];
|
||||
}
|
||||
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
NSMenuItem* CreateEditMenu()
|
||||
{
|
||||
NSMenu* menu = [[NSMenu alloc] initWithTitle:@"Edit"];
|
||||
|
||||
[menu addItemWithTitle:@"Undo"
|
||||
action:@selector(undo:)
|
||||
keyEquivalent:@"z"];
|
||||
[menu addItemWithTitle:@"Redo"
|
||||
action:@selector(redo:)
|
||||
keyEquivalent:@"Z"];
|
||||
[menu addItem:[NSMenuItem separatorItem]];
|
||||
[menu addItemWithTitle:@"Cut"
|
||||
action:@selector(cut:)
|
||||
keyEquivalent:@"x"];
|
||||
[menu addItemWithTitle:@"Copy"
|
||||
action:@selector(copy:)
|
||||
keyEquivalent:@"c"];
|
||||
[menu addItemWithTitle:@"Paste"
|
||||
action:@selector(paste:)
|
||||
keyEquivalent:@"v"];
|
||||
[menu addItemWithTitle:@"Delete"
|
||||
action:@selector(delete:)
|
||||
keyEquivalent:@""];
|
||||
[menu addItemWithTitle:@"Select All"
|
||||
action:@selector(selectAll:)
|
||||
keyEquivalent:@"a"];
|
||||
|
||||
NSMenuItem* menuItem = [NSMenuItem new];
|
||||
[menuItem setSubmenu:menu];
|
||||
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
NSMenuItem* CreateWindowMenu()
|
||||
{
|
||||
NSMenu* menu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||
[NSApp setWindowsMenu:menu];
|
||||
|
||||
[menu addItemWithTitle:@"Minimize"
|
||||
action:@selector(performMiniaturize:)
|
||||
keyEquivalent:@"m"];
|
||||
[menu addItemWithTitle:@"Zoom"
|
||||
action:@selector(performZoom:)
|
||||
keyEquivalent:@""];
|
||||
[menu addItem:[NSMenuItem separatorItem]];
|
||||
[menu addItemWithTitle:@"Bring All to Front"
|
||||
action:@selector(arrangeInFront:)
|
||||
keyEquivalent:@""];
|
||||
|
||||
NSMenuItem* menuItem = [NSMenuItem new];
|
||||
[menuItem setSubmenu:menu];
|
||||
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
void CreateMenu()
|
||||
{
|
||||
NSMenu* menuBar = [NSMenu new];
|
||||
[menuBar addItem:CreateApplicationMenu()];
|
||||
[menuBar addItem:CreateEditMenu()];
|
||||
[menuBar addItem:CreateWindowMenu()];
|
||||
|
||||
[NSApp setMainMenu:menuBar];
|
||||
}
|
||||
|
||||
DarwinVersion GetDarwinVersion()
|
||||
{
|
||||
DarwinVersion result = {};
|
||||
|
||||
int mib[2] = { CTL_KERN, KERN_OSRELEASE };
|
||||
size_t size = 0;
|
||||
|
||||
if (0 == sysctl(mib, 2, NULL, &size, NULL, 0))
|
||||
{
|
||||
char* version = static_cast<char*>(alloca(size));
|
||||
|
||||
if (0 == sysctl(mib, 2, version, &size, NULL, 0))
|
||||
{
|
||||
sscanf(version, "%hu.%hu.%hu",
|
||||
&result.major, &result.minor, &result.bugfix);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ReleaseApplicationController()
|
||||
{
|
||||
if (NULL != appCtrl)
|
||||
{
|
||||
[NSApp setDelegate:nil];
|
||||
[NSApp deactivate];
|
||||
|
||||
[appCtrl release];
|
||||
appCtrl = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
|
||||
const DarwinVersion darwinVersion = GetDarwinVersion();
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
for (int i = 0; i <= argc; ++i)
|
||||
{
|
||||
const char* const argument = argv[i];
|
||||
|
||||
if (NULL == argument || '\0' == argument[0])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (0 == strcmp(argument, "-wad_picker_restart"))
|
||||
{
|
||||
s_restartedFromWADPicker = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_argvStorage.Push(argument);
|
||||
s_argv[s_argc++] = s_argvStorage.Last().LockBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
[NSApplication sharedApplication];
|
||||
|
||||
// The following code isn't mandatory,
|
||||
// but it enables to run the application without a bundle
|
||||
if ([NSApp respondsToSelector:@selector(setActivationPolicy:)])
|
||||
{
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
}
|
||||
|
||||
CreateMenu();
|
||||
|
||||
atterm(ReleaseApplicationController);
|
||||
|
||||
appCtrl = [ApplicationController new];
|
||||
[NSApp setDelegate:appCtrl];
|
||||
[NSApp run];
|
||||
|
||||
[pool release];
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
1226
src/posix/cocoa/i_video.mm
Normal file
1226
src/posix/cocoa/i_video.mm
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue