mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-24 10:40:46 +00:00
- added the Posix backend code.
Nothing is hooked up yet and adjusted - next thing to do.
This commit is contained in:
parent
93d634813d
commit
5da0f5c7fa
38 changed files with 17987 additions and 1303 deletions
|
@ -1,373 +0,0 @@
|
|||
/* SDLMain.m - main entry point for our Cocoa-ized SDL app
|
||||
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
|
||||
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
|
||||
|
||||
Feel free to customize this file to suit your needs
|
||||
|
||||
Modified for EDuke32
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#include "sdl_inc.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include <sys/param.h> /* for MAXPATHLEN */
|
||||
|
||||
/* For some reason, Apple removed setAppleMenu from the headers in 10.4,
|
||||
but the method still is there and works. To avoid warnings, we declare
|
||||
it ourselves here. */
|
||||
@interface NSApplication(SDL_Missing_Methods)
|
||||
- (void)setAppleMenu:(NSMenu *)menu;
|
||||
@end
|
||||
|
||||
|
||||
/* Use this flag to determine whether we use CPS (docking) or not */
|
||||
#define SDL_USE_CPS
|
||||
#ifdef SDL_USE_CPS
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Portions of CPS.h */
|
||||
typedef struct CPSProcessSerNum
|
||||
{
|
||||
UInt32 lo;
|
||||
UInt32 hi;
|
||||
} CPSProcessSerNum;
|
||||
|
||||
extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
|
||||
extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
|
||||
extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SDL_USE_CPS */
|
||||
|
||||
static int gArgc;
|
||||
static char **gArgv;
|
||||
static BOOL gFinderLaunch;
|
||||
static BOOL gCalledAppMainline = FALSE;
|
||||
|
||||
static id nsapp;
|
||||
|
||||
static NSString *getApplicationName(void)
|
||||
{
|
||||
const NSDictionary *dict;
|
||||
NSString *appName = 0;
|
||||
|
||||
/* Determine the application name */
|
||||
dict = (const NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
|
||||
if (dict)
|
||||
appName = [dict objectForKey: @"CFBundleName"];
|
||||
|
||||
if (![appName length])
|
||||
appName = [[NSProcessInfo processInfo] processName];
|
||||
|
||||
return appName;
|
||||
}
|
||||
|
||||
@interface NSApplication (SDLApplication)
|
||||
@end
|
||||
|
||||
@implementation NSApplication (SDLApplication)
|
||||
/* Invoked from the Quit menu item */
|
||||
- (void)terminateCall:(id)sender
|
||||
{
|
||||
/* Post a SDL_QUIT event */
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
|
||||
UNREFERENCED_PARAMETER(sender);
|
||||
}
|
||||
@end
|
||||
|
||||
@interface SDLMain : NSObject <NSFileManagerDelegate>
|
||||
@end
|
||||
|
||||
/* The main class of the application, the application's delegate */
|
||||
@implementation SDLMain
|
||||
|
||||
/* Set the working directory to the .app's parent directory */
|
||||
- (void) setupWorkingDirectory:(BOOL)shouldChdir
|
||||
{
|
||||
if (shouldChdir)
|
||||
{
|
||||
char parentdir[MAXPATHLEN];
|
||||
CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
|
||||
CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
|
||||
if (CFURLGetFileSystemRepresentation(url2, 1, (UInt8 *)parentdir, MAXPATHLEN)) {
|
||||
chdir(parentdir); /* chdir to the binary app's parent */
|
||||
}
|
||||
CFRelease(url);
|
||||
CFRelease(url2);
|
||||
}
|
||||
}
|
||||
|
||||
static void setApplicationMenu(void)
|
||||
{
|
||||
/* warning: this code is very odd */
|
||||
NSMenu *appleMenu;
|
||||
NSMenuItem *menuItem;
|
||||
NSString *title;
|
||||
NSString *appName;
|
||||
|
||||
appName = getApplicationName();
|
||||
appleMenu = [[NSMenu alloc] initWithTitle:@""];
|
||||
|
||||
/* Add menu items */
|
||||
title = [@"About " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Hide " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
|
||||
|
||||
menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
|
||||
[menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
|
||||
|
||||
[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Quit " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(terminateCall:) keyEquivalent:@"q"];
|
||||
|
||||
|
||||
/* Put menu into the menubar */
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
|
||||
[menuItem setSubmenu:appleMenu];
|
||||
[[nsapp mainMenu] addItem:menuItem];
|
||||
|
||||
/* Tell the application object that this is now the application menu */
|
||||
[nsapp setAppleMenu:appleMenu];
|
||||
|
||||
/* Finally give up our references to the objects */
|
||||
[appleMenu release];
|
||||
[menuItem release];
|
||||
}
|
||||
|
||||
/* Create a window menu */
|
||||
static void setupWindowMenu(void)
|
||||
{
|
||||
NSMenu *windowMenu;
|
||||
NSMenuItem *windowMenuItem;
|
||||
NSMenuItem *menuItem;
|
||||
|
||||
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||
|
||||
/* "Minimize" item */
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
|
||||
[windowMenu addItem:menuItem];
|
||||
[menuItem release];
|
||||
|
||||
/* Put menu into the menubar */
|
||||
windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
|
||||
[windowMenuItem setSubmenu:windowMenu];
|
||||
[[nsapp mainMenu] addItem:windowMenuItem];
|
||||
|
||||
/* Tell the application object that this is now the window menu */
|
||||
[nsapp setWindowsMenu:windowMenu];
|
||||
|
||||
/* Finally give up our references to the objects */
|
||||
[windowMenu release];
|
||||
[windowMenuItem release];
|
||||
}
|
||||
|
||||
/* Replacement for NSApplicationMain */
|
||||
static void CustomApplicationMain (int argc, char **argv)
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
SDLMain *sdlMain;
|
||||
|
||||
/* Ensure the application object is initialised */
|
||||
nsapp = [NSApplication sharedApplication];
|
||||
|
||||
#ifdef SDL_USE_CPS
|
||||
{
|
||||
CPSProcessSerNum PSN;
|
||||
/* Tell the dock about us */
|
||||
if (!CPSGetCurrentProcess(&PSN))
|
||||
if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
|
||||
if (!CPSSetFrontProcess(&PSN))
|
||||
[NSApplication sharedApplication];
|
||||
}
|
||||
#endif /* SDL_USE_CPS */
|
||||
|
||||
/* Set up the menubar */
|
||||
[nsapp setMainMenu:[[NSMenu alloc] init]];
|
||||
setApplicationMenu();
|
||||
setupWindowMenu();
|
||||
|
||||
/* Create SDLMain and make it the app delegate */
|
||||
sdlMain = [[SDLMain alloc] init];
|
||||
[nsapp setDelegate:sdlMain];
|
||||
|
||||
/* Start the main event loop */
|
||||
[nsapp run];
|
||||
|
||||
[sdlMain release];
|
||||
[pool release];
|
||||
|
||||
UNREFERENCED_PARAMETER(argc);
|
||||
UNREFERENCED_PARAMETER(argv);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Catch document open requests...this lets us notice files when the app
|
||||
* was launched by double-clicking a document, or when a document was
|
||||
* dragged/dropped on the app's icon. You need to have a
|
||||
* CFBundleDocumentsType section in your Info.plist to get this message,
|
||||
* apparently.
|
||||
*
|
||||
* Files are added to gArgv, so to the app, they'll look like command line
|
||||
* arguments. Previously, apps launched from the finder had nothing but
|
||||
* an argv[0].
|
||||
*
|
||||
* This message may be received multiple times to open several docs on launch.
|
||||
*
|
||||
* This message is ignored once the app's mainline has been called.
|
||||
*/
|
||||
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
|
||||
{
|
||||
const char *temparg;
|
||||
size_t arglen;
|
||||
char *arg;
|
||||
char **newargv;
|
||||
|
||||
if (!gFinderLaunch) /* MacOS is passing command line args. */
|
||||
return FALSE;
|
||||
|
||||
if (gCalledAppMainline) /* app has started, ignore this document. */
|
||||
return FALSE;
|
||||
|
||||
temparg = [filename UTF8String];
|
||||
arglen = SDL_strlen(temparg) + 1;
|
||||
arg = (char *) SDL_malloc(arglen);
|
||||
if (arg == NULL)
|
||||
return FALSE;
|
||||
|
||||
newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
|
||||
if (newargv == NULL)
|
||||
{
|
||||
SDL_free(arg);
|
||||
return FALSE;
|
||||
}
|
||||
gArgv = newargv;
|
||||
|
||||
SDL_strlcpy(arg, temparg, arglen);
|
||||
gArgv[gArgc++] = arg;
|
||||
gArgv[gArgc] = NULL;
|
||||
|
||||
UNREFERENCED_PARAMETER(theApplication);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* Called when the internal event loop has just started running */
|
||||
- (void) applicationDidFinishLaunching: (NSNotification *) note
|
||||
{
|
||||
int status;
|
||||
|
||||
/* Set the working directory to the .app's parent directory */
|
||||
[self setupWorkingDirectory:gFinderLaunch];
|
||||
|
||||
/* Hand off to main application code */
|
||||
gCalledAppMainline = TRUE;
|
||||
status = SDL_main (gArgc, gArgv);
|
||||
|
||||
UNREFERENCED_PARAMETER(note);
|
||||
|
||||
/* We're done, thank you for playing */
|
||||
exit(status);
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@implementation NSString (ReplaceSubString)
|
||||
|
||||
- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
|
||||
{
|
||||
unsigned int bufferSize;
|
||||
unsigned int selfLen = [self length];
|
||||
unsigned int aStringLen = [aString length];
|
||||
unichar *buffer;
|
||||
NSRange localRange;
|
||||
NSString *result;
|
||||
|
||||
bufferSize = selfLen + aStringLen - aRange.length;
|
||||
buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar));
|
||||
|
||||
/* Get first part into buffer */
|
||||
localRange.location = 0;
|
||||
localRange.length = aRange.location;
|
||||
[self getCharacters:buffer range:localRange];
|
||||
|
||||
/* Get middle part into buffer */
|
||||
localRange.location = 0;
|
||||
localRange.length = aStringLen;
|
||||
[aString getCharacters:(buffer+aRange.location) range:localRange];
|
||||
|
||||
/* Get last part into buffer */
|
||||
localRange.location = aRange.location + aRange.length;
|
||||
localRange.length = selfLen - localRange.location;
|
||||
[self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
|
||||
|
||||
/* Build output string */
|
||||
result = [NSString stringWithCharacters:buffer length:bufferSize];
|
||||
|
||||
NSDeallocateMemoryPages(buffer, bufferSize);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
#ifdef main
|
||||
# undef main
|
||||
#endif
|
||||
|
||||
|
||||
/* Main entry point to executable - should *not* be SDL_main! */
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
/* Copy the arguments into a global variable */
|
||||
/* This is passed if we are launched by double-clicking */
|
||||
if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
|
||||
gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
|
||||
gArgv[0] = argv[0];
|
||||
gArgv[1] = NULL;
|
||||
gArgc = 1;
|
||||
gFinderLaunch = YES;
|
||||
} else {
|
||||
int i;
|
||||
gArgc = argc;
|
||||
gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
|
||||
for (i = 0; i <= argc; i++)
|
||||
gArgv[i] = argv[i];
|
||||
gFinderLaunch = NO;
|
||||
}
|
||||
|
||||
CustomApplicationMain (argc, argv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,16 +0,0 @@
|
|||
#ifndef osxbits_h_
|
||||
#define osxbits_h_
|
||||
#include <sys/types.h>
|
||||
|
||||
void osx_preopen(void);
|
||||
void osx_postopen(void);
|
||||
|
||||
int osx_msgbox(const char *name, const char *msg);
|
||||
int osx_ynbox(const char *name, const char *msg);
|
||||
|
||||
char *osx_gethomedir(void);
|
||||
char *osx_getsupportdir(int32_t local);
|
||||
char *osx_getappdir(void);
|
||||
char *osx_getapplicationsdir(int32_t local);
|
||||
|
||||
#endif
|
|
@ -1,179 +0,0 @@
|
|||
#include "compat.h"
|
||||
#include "osxbits.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#ifndef MAC_OS_X_VERSION_10_5
|
||||
# define NSImageScaleNone NSScaleNone
|
||||
#endif
|
||||
|
||||
#ifndef MAC_OS_X_VERSION_10_12
|
||||
# define NSEventModifierFlagOption NSAlternateKeyMask
|
||||
# define NSEventModifierFlagCommand NSCommandKeyMask
|
||||
# define NSEventMaskAny NSAnyEventMask
|
||||
# define NSWindowStyleMaskTitled NSTitledWindowMask
|
||||
# define NSWindowStyleMaskClosable NSClosableWindowMask
|
||||
# define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask
|
||||
# define NSWindowStyleMaskResizable NSResizableWindowMask
|
||||
# define NSAlertStyleInformational NSInformationalAlertStyle
|
||||
# define NSControlSizeSmall NSSmallControlSize
|
||||
#endif
|
||||
|
||||
#ifndef MAC_OS_VERSION_10_3
|
||||
# define MAC_OS_VERSION_10_3 1030
|
||||
#endif
|
||||
|
||||
id nsapp;
|
||||
|
||||
void osx_preopen(void)
|
||||
{
|
||||
// fix for "ld: absolute address to symbol _NSApp in a different linkage unit not supported"
|
||||
// (OS X 10.6) when building for PPC
|
||||
nsapp = [NSApplication sharedApplication];
|
||||
}
|
||||
|
||||
void osx_postopen(void)
|
||||
{
|
||||
[nsapp finishLaunching];
|
||||
}
|
||||
|
||||
int osx_msgbox(const char *name, const char *msg)
|
||||
{
|
||||
NSString *mmsg = [[NSString alloc] initWithUTF8String:msg];
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3
|
||||
NSAlert *alert = [[NSAlert alloc] init];
|
||||
[alert addButtonWithTitle: @"OK"];
|
||||
[alert setInformativeText: mmsg];
|
||||
[alert setAlertStyle: NSAlertStyleInformational];
|
||||
|
||||
[alert runModal];
|
||||
|
||||
[alert release];
|
||||
|
||||
#else
|
||||
NSRunAlertPanel(nil, mmsg, @"OK", nil, nil);
|
||||
#endif
|
||||
|
||||
[mmsg release];
|
||||
|
||||
UNREFERENCED_PARAMETER(name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osx_ynbox(const char *name, const char *msg)
|
||||
{
|
||||
NSString *mmsg = [[NSString alloc] initWithUTF8String:msg];
|
||||
int r;
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3
|
||||
NSAlert *alert = [[NSAlert alloc] init];
|
||||
|
||||
[alert addButtonWithTitle:@"Yes"];
|
||||
[alert addButtonWithTitle:@"No"];
|
||||
[alert setInformativeText: mmsg];
|
||||
[alert setAlertStyle: NSAlertStyleInformational];
|
||||
|
||||
r = ([alert runModal] == NSAlertFirstButtonReturn);
|
||||
|
||||
[alert release];
|
||||
#else
|
||||
r = (NSRunAlertPanel(nil, mmsg, @"Yes", @"No", nil) == NSAlertDefaultReturn);
|
||||
#endif
|
||||
|
||||
[mmsg release];
|
||||
|
||||
UNREFERENCED_PARAMETER(name);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
char *osx_gethomedir(void)
|
||||
{
|
||||
NSString *path = NSHomeDirectory();
|
||||
const char *Cpath = [path UTF8String];
|
||||
char *returnpath = NULL;
|
||||
|
||||
if (Cpath)
|
||||
returnpath = Bstrdup(Cpath);
|
||||
|
||||
[path release];
|
||||
|
||||
return returnpath;
|
||||
}
|
||||
|
||||
char *osx_getsupportdir(int32_t local)
|
||||
{
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, local ? NSUserDomainMask : NSLocalDomainMask, YES);
|
||||
char *returnpath = NULL;
|
||||
|
||||
if ([paths count] > 0)
|
||||
{
|
||||
const char *Cpath = [[paths objectAtIndex:0] UTF8String];
|
||||
|
||||
if (Cpath)
|
||||
returnpath = Bstrdup(Cpath);
|
||||
}
|
||||
|
||||
[paths release];
|
||||
|
||||
return returnpath;
|
||||
}
|
||||
|
||||
char *osx_getappdir(void)
|
||||
{
|
||||
CFBundleRef mainBundle;
|
||||
CFURLRef resUrl, fullUrl;
|
||||
CFStringRef str;
|
||||
const char *s;
|
||||
char *dir = NULL;
|
||||
|
||||
mainBundle = CFBundleGetMainBundle();
|
||||
if (!mainBundle) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
resUrl = CFBundleCopyResourcesDirectoryURL(mainBundle);
|
||||
CFRelease(mainBundle);
|
||||
if (!resUrl) {
|
||||
return NULL;
|
||||
}
|
||||
fullUrl = CFURLCopyAbsoluteURL(resUrl);
|
||||
if (fullUrl) {
|
||||
CFRelease(resUrl);
|
||||
resUrl = fullUrl;
|
||||
}
|
||||
|
||||
str = CFURLCopyFileSystemPath(resUrl, kCFURLPOSIXPathStyle);
|
||||
CFRelease(resUrl);
|
||||
if (!str) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s = CFStringGetCStringPtr(str, CFStringGetSystemEncoding());
|
||||
if (s) {
|
||||
dir = strdup(s);
|
||||
}
|
||||
CFRelease(str);
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
char *osx_getapplicationsdir(int32_t local)
|
||||
{
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, local ? NSUserDomainMask : NSLocalDomainMask, YES);
|
||||
char *returnpath = NULL;
|
||||
|
||||
if ([paths count] > 0)
|
||||
{
|
||||
const char *Cpath = [[paths objectAtIndex:0] UTF8String];
|
||||
|
||||
if (Cpath)
|
||||
returnpath = Bstrdup(Cpath);
|
||||
}
|
||||
|
||||
[paths release];
|
||||
|
||||
return returnpath;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
/* SDLMain.m - main entry point for our Cocoa-ized SDL app
|
||||
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
|
||||
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
|
||||
|
||||
Feel free to customize this file to suit your needs
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface SDLMain : NSObject
|
||||
@end
|
102
source/platform/posix/cocoa/gl_sysfb.h
Normal file
102
source/platform/posix/cocoa/gl_sysfb.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
** gl_sysfb.h
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2012-2018 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef COCOA_GL_SYSFB_H_INCLUDED
|
||||
#define COCOA_GL_SYSFB_H_INCLUDED
|
||||
|
||||
#include "v_video.h"
|
||||
|
||||
#ifdef __OBJC__
|
||||
@class NSCursor;
|
||||
@class CocoaWindow;
|
||||
#else
|
||||
typedef struct objc_object NSCursor;
|
||||
typedef struct objc_object CocoaWindow;
|
||||
#endif
|
||||
|
||||
class SystemBaseFrameBuffer : public DFrameBuffer
|
||||
{
|
||||
public:
|
||||
// This must have the same parameters as the Windows version, even if they are not used!
|
||||
SystemBaseFrameBuffer(void *hMonitor, bool fullscreen);
|
||||
~SystemBaseFrameBuffer();
|
||||
|
||||
bool IsFullscreen() override;
|
||||
|
||||
int GetClientWidth() override;
|
||||
int GetClientHeight() override;
|
||||
void ToggleFullscreen(bool yes) override;
|
||||
void SetWindowSize(int width, int height) override;
|
||||
|
||||
virtual void SetMode(bool fullscreen, bool hiDPI);
|
||||
|
||||
static void UseHiDPI(bool hiDPI);
|
||||
static void SetCursor(NSCursor* cursor);
|
||||
static void SetWindowVisible(bool visible);
|
||||
static void SetWindowTitle(const char* title);
|
||||
|
||||
void SetWindow(CocoaWindow* window) { m_window = window; }
|
||||
|
||||
protected:
|
||||
SystemBaseFrameBuffer() {}
|
||||
|
||||
void SetFullscreenMode();
|
||||
void SetWindowedMode();
|
||||
|
||||
bool m_fullscreen;
|
||||
bool m_hiDPI;
|
||||
|
||||
CocoaWindow* m_window;
|
||||
|
||||
int GetTitleBarHeight() const;
|
||||
|
||||
};
|
||||
|
||||
class SystemGLFrameBuffer : public SystemBaseFrameBuffer
|
||||
{
|
||||
typedef SystemBaseFrameBuffer Super;
|
||||
|
||||
public:
|
||||
SystemGLFrameBuffer(void *hMonitor, bool fullscreen);
|
||||
|
||||
void SetVSync(bool vsync) override;
|
||||
|
||||
void SetMode(bool fullscreen, bool hiDPI) override;
|
||||
|
||||
protected:
|
||||
void SwapBuffers();
|
||||
|
||||
SystemGLFrameBuffer() {}
|
||||
};
|
||||
|
||||
#endif // COCOA_GL_SYSFB_H_INCLUDED
|
61
source/platform/posix/cocoa/i_common.h
Normal file
61
source/platform/posix/cocoa/i_common.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
** i_common.h
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2012-2015 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef COCOA_I_COMMON_INCLUDED
|
||||
#define COCOA_I_COMMON_INCLUDED
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
|
||||
// Version of AppKit framework we are interested in
|
||||
// The following values are needed to build with earlier SDKs
|
||||
|
||||
#define AppKit10_7 1138
|
||||
#define AppKit10_8 1187
|
||||
#define AppKit10_9 1265
|
||||
|
||||
|
||||
@interface NSWindow(ExitAppOnClose)
|
||||
- (void)exitAppOnClose;
|
||||
@end
|
||||
|
||||
|
||||
void I_ProcessEvent(NSEvent* event);
|
||||
|
||||
void I_ProcessJoysticks();
|
||||
|
||||
NSSize I_GetContentViewSize(const NSWindow* window);
|
||||
void I_SetMainWindowVisible(bool visible);
|
||||
void I_SetNativeMouse(bool wantNative);
|
||||
|
||||
#endif // COCOA_I_COMMON_INCLUDED
|
804
source/platform/posix/cocoa/i_input.mm
Normal file
804
source/platform/posix/cocoa/i_input.mm
Normal file
|
@ -0,0 +1,804 @@
|
|||
/*
|
||||
** i_input.mm
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2012-2015 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 "i_common.h"
|
||||
|
||||
#import <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 "events.h"
|
||||
#include "g_game.h"
|
||||
#include "g_levellocals.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)
|
||||
|
||||
CVAR(Bool, k_allowfullscreentoggle, true, 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()
|
||||
{
|
||||
bool wantCapture = (MENU_Off == menuactive)
|
||||
? (c_down == ConsoleState || c_falling == ConsoleState || chatmodeon)
|
||||
: (MENU_On == menuactive || MENU_OnNoPause == menuactive);
|
||||
|
||||
// [ZZ] check active event handlers that want the UI processing
|
||||
if (!wantCapture && primaryLevel->localEventManager->CheckUiProcessors())
|
||||
{
|
||||
wantCapture = true;
|
||||
}
|
||||
|
||||
if (wantCapture != GUICapture)
|
||||
{
|
||||
GUICapture = wantCapture;
|
||||
|
||||
ResetButtonStates();
|
||||
}
|
||||
}
|
||||
|
||||
void SetCursorPosition(const NSPoint position)
|
||||
{
|
||||
NSWindow* window = [NSApp keyWindow];
|
||||
if (nil == window)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const NSRect displayRect = [[window screen] frame];
|
||||
const CGPoint eventPoint = CGPointMake(position.x, displayRect.size.height - position.y);
|
||||
|
||||
CGEventSourceRef eventSource = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
|
||||
|
||||
if (NULL != eventSource)
|
||||
{
|
||||
CGEventRef mouseMoveEvent = CGEventCreateMouseEvent(eventSource,
|
||||
kCGEventMouseMoved, eventPoint, kCGMouseButtonLeft);
|
||||
|
||||
if (NULL != mouseMoveEvent)
|
||||
{
|
||||
CGEventPost(kCGHIDEventTap, mouseMoveEvent);
|
||||
CFRelease(mouseMoveEvent);
|
||||
}
|
||||
|
||||
CFRelease(eventSource);
|
||||
}
|
||||
|
||||
// TODO: remove this magic!
|
||||
s_skipMouseMoves = 2;
|
||||
}
|
||||
|
||||
void CenterCursor()
|
||||
{
|
||||
NSWindow* window = [NSApp keyWindow];
|
||||
if (nil == window)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const NSRect displayRect = [[window screen] frame];
|
||||
const NSRect windowRect = [window frame];
|
||||
const NSPoint centerPoint = { NSMidX(windowRect), NSMidY(windowRect) };
|
||||
|
||||
SetCursorPosition(centerPoint);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (!wantNative && primaryLevel->localEventManager->CheckRequireMouse())
|
||||
wantNative = true;
|
||||
|
||||
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;
|
||||
static NSPoint mouseLocation;
|
||||
|
||||
if (wantNative != nativeMouse)
|
||||
{
|
||||
nativeMouse = wantNative;
|
||||
|
||||
if (!wantNative)
|
||||
{
|
||||
mouseLocation = [NSEvent mouseLocation];
|
||||
CenterCursor();
|
||||
}
|
||||
|
||||
CGAssociateMouseAndMouseCursorPosition(wantNative);
|
||||
|
||||
if (wantNative)
|
||||
{
|
||||
SetCursorPosition(mouseLocation);
|
||||
|
||||
[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;
|
||||
}
|
||||
|
||||
int16_t 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, unichar *realchar)
|
||||
{
|
||||
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';
|
||||
}
|
||||
|
||||
*realchar = unicodeCharacter;
|
||||
return character;
|
||||
}
|
||||
|
||||
void ProcessKeyboardEventInMenu(NSEvent* theEvent)
|
||||
{
|
||||
event_t event = {};
|
||||
|
||||
unichar realchar;
|
||||
event.type = EV_GUI_Event;
|
||||
event.subtype = NSKeyDown == [theEvent type] ? EV_GUI_KeyDown : EV_GUI_KeyUp;
|
||||
event.data2 = GetCharacterFromNSEvent(theEvent, &realchar);
|
||||
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 = realchar;
|
||||
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 NSRect screenRect = NSMakeRect(screenPos.x, screenPos.y, 0, 0);
|
||||
const NSRect windowRect = [window convertRectFromScreen:screenRect];
|
||||
|
||||
NSPoint viewPos;
|
||||
NSSize viewSize;
|
||||
CGFloat scale;
|
||||
|
||||
if (view.layer == nil)
|
||||
{
|
||||
viewPos = [view convertPointToBacking:windowRect.origin];
|
||||
viewSize = [view convertSizeToBacking:view.frame.size];
|
||||
scale = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
viewPos = windowRect.origin;
|
||||
viewSize = view.frame.size;
|
||||
scale = view.layer.contentsScale;
|
||||
}
|
||||
|
||||
const CGFloat posX = viewPos.x * scale;
|
||||
const CGFloat posY = (viewSize.height - viewPos.y) * scale;
|
||||
|
||||
outEvent->data1 = static_cast<int16_t>(posX);
|
||||
outEvent->data2 = static_cast<int16_t>(posY);
|
||||
|
||||
screen->ScaleCoordsFromWindow(outEvent->data1, outEvent->data2);
|
||||
}
|
||||
|
||||
void ProcessMouseMoveInMenu(NSEvent* theEvent)
|
||||
{
|
||||
event_t event = {};
|
||||
|
||||
event.type = EV_GUI_Event;
|
||||
event.subtype = EV_GUI_MouseMove;
|
||||
event.data3 = ModifierFlagsToGUIKeyModifiers(theEvent);
|
||||
|
||||
NSEventToGameMousePosition(theEvent, &event);
|
||||
|
||||
D_PostEvent(&event);
|
||||
}
|
||||
|
||||
void ProcessMouseMoveInGame(NSEvent* theEvent)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ProcessKeyboardEvent(NSEvent* theEvent)
|
||||
{
|
||||
const unsigned short keyCode = [theEvent keyCode];
|
||||
if (keyCode >= KEY_COUNT)
|
||||
{
|
||||
assert(!"Unknown keycode");
|
||||
return;
|
||||
}
|
||||
|
||||
const bool isARepeat = [theEvent isARepeat];
|
||||
|
||||
if (k_allowfullscreentoggle
|
||||
&& (kVK_ANSI_F == keyCode)
|
||||
&& (NSCommandKeyMask & [theEvent modifierFlags])
|
||||
&& (NSKeyDown == [theEvent type])
|
||||
&& !isARepeat)
|
||||
{
|
||||
ToggleFullscreen = !ToggleFullscreen;
|
||||
return;
|
||||
}
|
||||
|
||||
if (GUICapture)
|
||||
{
|
||||
ProcessKeyboardEventInMenu(theEvent);
|
||||
}
|
||||
else if (!isARepeat)
|
||||
{
|
||||
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 ProcessKeyboardFlagsEvent(NSEvent* theEvent)
|
||||
{
|
||||
if (GUICapture)
|
||||
{
|
||||
// Ignore events from modifier keys in menu/console/chat
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (DIK_CAPITAL == event.data1)
|
||||
{
|
||||
// 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
|
||||
event.type = EV_KeyDown;
|
||||
}
|
||||
|
||||
D_PostEvent(&event);
|
||||
}
|
||||
|
||||
|
||||
void ProcessMouseMoveEvent(NSEvent* theEvent)
|
||||
{
|
||||
if (!use_mouse)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_skipMouseMoves > 0)
|
||||
{
|
||||
--s_skipMouseMoves;
|
||||
return;
|
||||
}
|
||||
|
||||
if (GUICapture)
|
||||
{
|
||||
ProcessMouseMoveInMenu(theEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessMouseMoveInGame(theEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessMouseButtonEvent(NSEvent* theEvent)
|
||||
{
|
||||
if (!use_mouse)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
event_t event = {};
|
||||
|
||||
const NSEventType cocoaEventType = [theEvent type];
|
||||
|
||||
if (GUICapture)
|
||||
{
|
||||
event.type = EV_GUI_Event;
|
||||
event.data3 = ModifierFlagsToGUIKeyModifiers(theEvent);
|
||||
|
||||
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 ProcessMouseWheelEvent(NSEvent* theEvent)
|
||||
{
|
||||
if (!use_mouse)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int16_t modifiers = ModifierFlagsToGUIKeyModifiers(theEvent);
|
||||
const CGFloat delta = (modifiers & GKM_SHIFT)
|
||||
? [theEvent deltaX]
|
||||
: [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 = modifiers;
|
||||
}
|
||||
else
|
||||
{
|
||||
event.type = isZeroDelta ? EV_KeyUp : EV_KeyDown;
|
||||
event.data1 = delta > 0.0f ? KEY_MWHEELUP : KEY_MWHEELDOWN;
|
||||
}
|
||||
|
||||
D_PostEvent(&event);
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
|
||||
void I_ProcessEvent(NSEvent* event)
|
||||
{
|
||||
const NSEventType eventType = [event type];
|
||||
|
||||
switch (eventType)
|
||||
{
|
||||
case NSMouseMoved:
|
||||
ProcessMouseMoveEvent(event);
|
||||
break;
|
||||
|
||||
case NSLeftMouseDown:
|
||||
case NSLeftMouseUp:
|
||||
case NSRightMouseDown:
|
||||
case NSRightMouseUp:
|
||||
case NSOtherMouseDown:
|
||||
case NSOtherMouseUp:
|
||||
ProcessMouseButtonEvent(event);
|
||||
break;
|
||||
|
||||
case NSLeftMouseDragged:
|
||||
case NSRightMouseDragged:
|
||||
case NSOtherMouseDragged:
|
||||
ProcessMouseButtonEvent(event);
|
||||
ProcessMouseMoveEvent(event);
|
||||
break;
|
||||
|
||||
case NSScrollWheel:
|
||||
ProcessMouseWheelEvent(event);
|
||||
break;
|
||||
|
||||
case NSKeyDown:
|
||||
case NSKeyUp:
|
||||
ProcessKeyboardEvent(event);
|
||||
break;
|
||||
|
||||
case NSFlagsChanged:
|
||||
ProcessKeyboardFlagsEvent(event);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
1244
source/platform/posix/cocoa/i_joystick.cpp
Normal file
1244
source/platform/posix/cocoa/i_joystick.cpp
Normal file
File diff suppressed because it is too large
Load diff
519
source/platform/posix/cocoa/i_main.mm
Normal file
519
source/platform/posix/cocoa/i_main.mm
Normal file
|
@ -0,0 +1,519 @@
|
|||
/*
|
||||
** i_main.mm
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2012-2018 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 "i_common.h"
|
||||
#include "s_sound.h"
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include "c_console.h"
|
||||
#include "c_cvars.h"
|
||||
#include "cmdlib.h"
|
||||
#include "d_main.h"
|
||||
#include "i_system.h"
|
||||
#include "m_argv.h"
|
||||
#include "st_console.h"
|
||||
#include "version.h"
|
||||
#include "doomerrors.h"
|
||||
#include "s_music.h"
|
||||
|
||||
|
||||
#define ZD_UNUSED(VARIABLE) ((void)(VARIABLE))
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
CVAR (Bool, i_soundinbackground, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
EXTERN_CVAR(Int, vid_defwidth )
|
||||
EXTERN_CVAR(Int, vid_defheight)
|
||||
EXTERN_CVAR(Bool, vid_vsync )
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
void Mac_I_FatalError(const char* const message)
|
||||
{
|
||||
I_SetMainWindowVisible(false);
|
||||
S_StopMusic(true);
|
||||
|
||||
FConsoleWindow::GetInstance().ShowFatalError(message);
|
||||
}
|
||||
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < 101000
|
||||
|
||||
// Available since 10.9 with no public declaration/definition until 10.10
|
||||
|
||||
struct NSOperatingSystemVersion
|
||||
{
|
||||
NSInteger majorVersion;
|
||||
NSInteger minorVersion;
|
||||
NSInteger patchVersion;
|
||||
};
|
||||
|
||||
@interface NSProcessInfo(OperatingSystemVersion)
|
||||
- (NSOperatingSystemVersion)operatingSystemVersion;
|
||||
@end
|
||||
|
||||
#endif // before 10.10
|
||||
|
||||
void I_DetectOS()
|
||||
{
|
||||
NSOperatingSystemVersion version = {};
|
||||
NSProcessInfo* const processInfo = [NSProcessInfo processInfo];
|
||||
|
||||
if ([processInfo respondsToSelector:@selector(operatingSystemVersion)])
|
||||
{
|
||||
version = [processInfo operatingSystemVersion];
|
||||
}
|
||||
|
||||
const char* name = "Unknown version";
|
||||
|
||||
if (10 == version.majorVersion) switch (version.minorVersion)
|
||||
{
|
||||
case 7: name = "Mac OS X Lion"; break;
|
||||
case 8: name = "OS X Mountain Lion"; break;
|
||||
case 9: name = "OS X Mavericks"; break;
|
||||
case 10: name = "OS X Yosemite"; break;
|
||||
case 11: name = "OS X El Capitan"; break;
|
||||
case 12: name = "macOS Sierra"; break;
|
||||
case 13: name = "macOS High Sierra"; break;
|
||||
case 14: name = "macOS Mojave"; break;
|
||||
case 15: name = "macOS Catalina"; break;
|
||||
}
|
||||
|
||||
char release[16] = "unknown";
|
||||
size_t size = sizeof release - 1;
|
||||
sysctlbyname("kern.osversion", release, &size, nullptr, 0);
|
||||
|
||||
char model[64] = "Unknown Mac model";
|
||||
size = sizeof model - 1;
|
||||
sysctlbyname("hw.model", model, &size, nullptr, 0);
|
||||
|
||||
const char* const architecture =
|
||||
#ifdef __i386__
|
||||
"32-bit Intel";
|
||||
#elif defined __x86_64__
|
||||
"64-bit Intel";
|
||||
#else
|
||||
"Unknown";
|
||||
#endif
|
||||
|
||||
Printf("%s running %s %d.%d.%d (%s) %s\n", model, name,
|
||||
int(version.majorVersion), int(version.minorVersion), int(version.patchVersion),
|
||||
release, architecture);
|
||||
}
|
||||
|
||||
|
||||
FArgs* Args; // command line arguments
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
TArray<FString> s_argv;
|
||||
|
||||
int DoMain(int argc, char** argv)
|
||||
{
|
||||
printf(GAMENAME" %s - %s - Cocoa version\nCompiled on %s\n\n",
|
||||
GetVersionString(), GetGitTime(), __DATE__);
|
||||
|
||||
seteuid(getuid());
|
||||
|
||||
// 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;
|
||||
|
||||
Args = new FArgs(argc, argv);
|
||||
|
||||
NSString* exePath = [[NSBundle mainBundle] executablePath];
|
||||
progdir = [[exePath stringByDeletingLastPathComponent] UTF8String];
|
||||
progdir += "/";
|
||||
|
||||
auto ret = D_DoomMain();
|
||||
FConsoleWindow::DeleteInstance();
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@interface ApplicationController : NSResponder<NSApplicationDelegate>
|
||||
{
|
||||
}
|
||||
|
||||
- (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;
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
|
||||
|
||||
- (void)sendExitEvent:(id)sender;
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
|
||||
extern bool AppActive;
|
||||
|
||||
- (void)applicationDidBecomeActive:(NSNotification*)aNotification
|
||||
{
|
||||
ZD_UNUSED(aNotification);
|
||||
|
||||
S_SetSoundPaused(1);
|
||||
|
||||
AppActive = true;
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(NSNotification*)aNotification
|
||||
{
|
||||
ZD_UNUSED(aNotification);
|
||||
|
||||
S_SetSoundPaused(i_soundinbackground);
|
||||
|
||||
AppActive = false;
|
||||
}
|
||||
|
||||
|
||||
- (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];
|
||||
|
||||
FConsoleWindow::CreateInstance();
|
||||
|
||||
const size_t argc = s_argv.Size();
|
||||
TArray<char*> argv(argc + 1, true);
|
||||
|
||||
for (size_t i = 0; i < argc; ++i)
|
||||
{
|
||||
argv[i] = s_argv[i].LockBuffer();
|
||||
}
|
||||
|
||||
argv[argc] = nullptr;
|
||||
|
||||
exit(DoMain(argc, &argv[0]));
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename
|
||||
{
|
||||
ZD_UNUSED(theApplication);
|
||||
|
||||
// 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 (size_t i = 0, count = s_argv.Size(); i < count; ++i)
|
||||
{
|
||||
if (0 == strcmp(s_argv[i], charFileName))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
bool iwad = false;
|
||||
|
||||
if (const char* const extPos = strrchr(charFileName, '.'))
|
||||
{
|
||||
iwad = 0 == stricmp(extPos, ".iwad")
|
||||
|| 0 == stricmp(extPos, ".ipk3")
|
||||
|| 0 == stricmp(extPos, ".ipk7");
|
||||
}
|
||||
|
||||
s_argv.Push(iwad ? "-iwad" : "-file");
|
||||
s_argv.Push(charFileName);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
I_ProcessEvent(event);
|
||||
|
||||
[NSApp sendEvent:event];
|
||||
}
|
||||
|
||||
[NSApp updateWindows];
|
||||
|
||||
[pool release];
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
|
||||
{
|
||||
[self sendExitEvent:sender];
|
||||
return NSTerminateLater;
|
||||
}
|
||||
|
||||
- (void)sendExitEvent:(id)sender
|
||||
{
|
||||
throw CExitEvent(0);
|
||||
}
|
||||
|
||||
@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(sendExitEvent:)
|
||||
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];
|
||||
}
|
||||
|
||||
void ReleaseApplicationController()
|
||||
{
|
||||
if (NULL != appCtrl)
|
||||
{
|
||||
[NSApp setDelegate:nil];
|
||||
[NSApp deactivate];
|
||||
|
||||
[appCtrl release];
|
||||
appCtrl = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
for (int i = 0; i < argc; ++i)
|
||||
{
|
||||
const char* const argument = argv[i];
|
||||
|
||||
#if _DEBUG
|
||||
if (0 == strcmp(argument, "-wait_for_debugger"))
|
||||
{
|
||||
NSAlert* alert = [[NSAlert alloc] init];
|
||||
[alert setMessageText:@GAMENAME];
|
||||
[alert setInformativeText:@"Waiting for debugger..."];
|
||||
[alert addButtonWithTitle:@"Continue"];
|
||||
[alert runModal];
|
||||
}
|
||||
#endif // _DEBUG
|
||||
|
||||
s_argv.Push(argument);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
atexit(ReleaseApplicationController);
|
||||
|
||||
appCtrl = [ApplicationController new];
|
||||
[NSApp setDelegate:appCtrl];
|
||||
[NSApp run];
|
||||
|
||||
[pool release];
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
304
source/platform/posix/cocoa/i_system.mm
Normal file
304
source/platform/posix/cocoa/i_system.mm
Normal file
|
@ -0,0 +1,304 @@
|
|||
/*
|
||||
** i_system.mm
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2012-2018 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 "i_common.h"
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include "d_protocol.h"
|
||||
#include "doomdef.h"
|
||||
#include "doomerrors.h"
|
||||
#include "doomstat.h"
|
||||
#include "g_game.h"
|
||||
#include "gameconfigfile.h"
|
||||
#include "i_sound.h"
|
||||
#include "i_system.h"
|
||||
#include "st_console.h"
|
||||
#include "v_text.h"
|
||||
#include "x86.h"
|
||||
#include "cmdlib.h"
|
||||
|
||||
|
||||
void I_Tactile(int /*on*/, int /*off*/, int /*total*/)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ticcmd_t* I_BaseTiccmd()
|
||||
{
|
||||
static ticcmd_t emptycmd;
|
||||
return &emptycmd;
|
||||
}
|
||||
|
||||
|
||||
|
||||
double PerfToSec, PerfToMillisec;
|
||||
|
||||
static void CalculateCPUSpeed()
|
||||
{
|
||||
long long frequency;
|
||||
size_t size = sizeof frequency;
|
||||
|
||||
if (0 == sysctlbyname("machdep.tsc.frequency", &frequency, &size, nullptr, 0) && 0 != frequency)
|
||||
{
|
||||
PerfToSec = 1.0 / frequency;
|
||||
PerfToMillisec = 1000.0 / frequency;
|
||||
|
||||
if (!batchrun)
|
||||
{
|
||||
Printf("CPU speed: %.0f MHz\n", 0.001 / PerfToMillisec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void I_Init(void)
|
||||
{
|
||||
CheckCPUID(&CPU);
|
||||
CalculateCPUSpeed();
|
||||
DumpCPUInfo(&CPU);
|
||||
}
|
||||
|
||||
void I_SetIWADInfo()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void I_DebugPrint(const char *cp)
|
||||
{
|
||||
NSLog(@"%s", cp);
|
||||
}
|
||||
|
||||
|
||||
void I_PrintStr(const char* const message)
|
||||
{
|
||||
FConsoleWindow::GetInstance().AddText(message);
|
||||
|
||||
// Strip out any color escape sequences before writing to output
|
||||
char* const copy = new char[strlen(message) + 1];
|
||||
const char* srcp = message;
|
||||
char* dstp = copy;
|
||||
|
||||
while ('\0' != *srcp)
|
||||
{
|
||||
if (TEXTCOLOR_ESCAPE == *srcp)
|
||||
{
|
||||
if ('\0' != srcp[1])
|
||||
{
|
||||
srcp += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (0x1d == *srcp || 0x1f == *srcp) // Opening and closing bar character
|
||||
{
|
||||
*dstp++ = '-';
|
||||
++srcp;
|
||||
}
|
||||
else if (0x1e == *srcp) // Middle bar character
|
||||
{
|
||||
*dstp++ = '=';
|
||||
++srcp;
|
||||
}
|
||||
else
|
||||
{
|
||||
*dstp++ = *srcp++;
|
||||
}
|
||||
}
|
||||
|
||||
*dstp = '\0';
|
||||
|
||||
fputs(copy, stdout);
|
||||
delete[] copy;
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
|
||||
void Mac_I_FatalError(const char* const message);
|
||||
|
||||
void I_ShowFatalError(const char *message)
|
||||
{
|
||||
Mac_I_FatalError(message);
|
||||
}
|
||||
|
||||
|
||||
int I_PickIWad(WadStuff* const wads, const int numwads, const bool showwin, const int defaultiwad)
|
||||
{
|
||||
if (!showwin)
|
||||
{
|
||||
return defaultiwad;
|
||||
}
|
||||
|
||||
I_SetMainWindowVisible(false);
|
||||
|
||||
extern int I_PickIWad_Cocoa(WadStuff*, int, bool, int);
|
||||
const int result = I_PickIWad_Cocoa(wads, numwads, showwin, defaultiwad);
|
||||
|
||||
I_SetMainWindowVisible(true);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool I_WriteIniFailed()
|
||||
{
|
||||
printf("The config file %s could not be saved:\n%s\n", GameConfig->GetPathName(), strerror(errno));
|
||||
return false; // return true to retry
|
||||
}
|
||||
|
||||
|
||||
static const char *pattern;
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < 1080
|
||||
static int matchfile(struct dirent *ent)
|
||||
#else
|
||||
static int matchfile(const struct dirent *ent)
|
||||
#endif
|
||||
{
|
||||
return fnmatch(pattern, ent->d_name, FNM_NOESCAPE) == 0;
|
||||
}
|
||||
|
||||
void* I_FindFirst(const char* const filespec, findstate_t* const fileinfo)
|
||||
{
|
||||
FString dir;
|
||||
|
||||
const char* const slash = strrchr(filespec, '/');
|
||||
|
||||
if (slash)
|
||||
{
|
||||
pattern = slash+1;
|
||||
dir = FString(filespec, slash - filespec + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
pattern = filespec;
|
||||
dir = ".";
|
||||
}
|
||||
|
||||
fileinfo->current = 0;
|
||||
fileinfo->count = scandir(dir.GetChars(), &fileinfo->namelist, matchfile, alphasort);
|
||||
|
||||
if (fileinfo->count > 0)
|
||||
{
|
||||
return fileinfo;
|
||||
}
|
||||
|
||||
return (void*)-1;
|
||||
}
|
||||
|
||||
int I_FindNext(void* const handle, findstate_t* const fileinfo)
|
||||
{
|
||||
findstate_t* const state = static_cast<findstate_t*>(handle);
|
||||
|
||||
if (state->current < fileinfo->count)
|
||||
{
|
||||
return ++state->current < fileinfo->count ? 0 : -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int I_FindClose(void* const handle)
|
||||
{
|
||||
findstate_t* const state = static_cast<findstate_t*>(handle);
|
||||
|
||||
if (handle != (void*)-1 && state->count > 0)
|
||||
{
|
||||
for (int i = 0; i < state->count; ++i)
|
||||
{
|
||||
free(state->namelist[i]);
|
||||
}
|
||||
|
||||
free(state->namelist);
|
||||
state->namelist = NULL;
|
||||
state->count = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int I_FindAttr(findstate_t* const fileinfo)
|
||||
{
|
||||
dirent* const ent = fileinfo->namelist[fileinfo->current];
|
||||
bool isdir;
|
||||
|
||||
if (DirEntryExists(ent->d_name, &isdir))
|
||||
{
|
||||
return isdir ? FA_DIREC : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void I_PutInClipboard(const char* const string)
|
||||
{
|
||||
NSPasteboard* const pasteBoard = [NSPasteboard generalPasteboard];
|
||||
NSString* const stringType = NSStringPboardType;
|
||||
NSArray* const types = [NSArray arrayWithObjects:stringType, nil];
|
||||
NSString* const content = [NSString stringWithUTF8String:string];
|
||||
|
||||
[pasteBoard declareTypes:types
|
||||
owner:nil];
|
||||
[pasteBoard setString:content
|
||||
forType:stringType];
|
||||
}
|
||||
|
||||
FString I_GetFromClipboard(bool returnNothing)
|
||||
{
|
||||
if (returnNothing)
|
||||
{
|
||||
return FString();
|
||||
}
|
||||
|
||||
NSPasteboard* const pasteBoard = [NSPasteboard generalPasteboard];
|
||||
NSString* const value = [pasteBoard stringForType:NSStringPboardType];
|
||||
|
||||
return FString([value UTF8String]);
|
||||
}
|
||||
|
||||
|
||||
unsigned int I_MakeRNGSeed()
|
||||
{
|
||||
return static_cast<unsigned int>(arc4random());
|
||||
}
|
||||
|
||||
|
||||
TArray<FString> I_GetGogPaths()
|
||||
{
|
||||
// GOG's Doom games are Windows only at the moment
|
||||
return TArray<FString>();
|
||||
}
|
||||
|
1072
source/platform/posix/cocoa/i_video.mm
Normal file
1072
source/platform/posix/cocoa/i_video.mm
Normal file
File diff suppressed because it is too large
Load diff
96
source/platform/posix/cocoa/st_console.h
Normal file
96
source/platform/posix/cocoa/st_console.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
** st_console.h
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2015 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef COCOA_ST_CONSOLE_INCLUDED
|
||||
#define COCOA_ST_CONSOLE_INCLUDED
|
||||
|
||||
@class NSButton;
|
||||
@class NSProgressIndicator;
|
||||
@class NSScrollView;
|
||||
@class NSTextField;
|
||||
@class NSTextView;
|
||||
@class NSView;
|
||||
@class NSWindow;
|
||||
|
||||
struct PalEntry;
|
||||
|
||||
|
||||
class FConsoleWindow
|
||||
{
|
||||
public:
|
||||
static FConsoleWindow& GetInstance();
|
||||
|
||||
static void CreateInstance();
|
||||
static void DeleteInstance();
|
||||
|
||||
void Show(bool visible);
|
||||
void ShowFatalError(const char* message);
|
||||
|
||||
void AddText(const char* message);
|
||||
|
||||
void SetTitleText();
|
||||
void SetProgressBar(bool visible);
|
||||
|
||||
// FStartupScreen functionality
|
||||
void Progress(int current, int maximum);
|
||||
void NetInit(const char* message, int playerCount);
|
||||
void NetProgress(int count);
|
||||
void NetDone();
|
||||
|
||||
private:
|
||||
NSWindow* m_window;
|
||||
NSTextView* m_textView;
|
||||
NSScrollView* m_scrollView;
|
||||
NSProgressIndicator* m_progressBar;
|
||||
|
||||
NSView* m_netView;
|
||||
NSTextField* m_netMessageText;
|
||||
NSTextField* m_netCountText;
|
||||
NSProgressIndicator* m_netProgressBar;
|
||||
NSButton* m_netAbortButton;
|
||||
|
||||
unsigned int m_characterCount;
|
||||
|
||||
int m_netCurPos;
|
||||
int m_netMaxPos;
|
||||
|
||||
FConsoleWindow();
|
||||
|
||||
void ExpandTextView(float height);
|
||||
|
||||
void AddText(const PalEntry& color, const char* message);
|
||||
|
||||
void ScrollTextToBottom();
|
||||
};
|
||||
|
||||
#endif // COCOA_ST_CONSOLE_INCLUDED
|
532
source/platform/posix/cocoa/st_console.mm
Normal file
532
source/platform/posix/cocoa/st_console.mm
Normal file
|
@ -0,0 +1,532 @@
|
|||
/*
|
||||
** st_console.mm
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2015 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 "i_common.h"
|
||||
|
||||
#include "d_main.h"
|
||||
#include "st_console.h"
|
||||
#include "v_text.h"
|
||||
#include "version.h"
|
||||
#include "i_time.h"
|
||||
|
||||
|
||||
static NSColor* RGB(const uint8_t red, const uint8_t green, const uint8_t blue)
|
||||
{
|
||||
return [NSColor colorWithCalibratedRed:red / 255.0f
|
||||
green:green / 255.0f
|
||||
blue:blue / 255.0f
|
||||
alpha:1.0f];
|
||||
}
|
||||
|
||||
static NSColor* RGB(const PalEntry& color)
|
||||
{
|
||||
return RGB(color.r, color.g, color.b);
|
||||
}
|
||||
|
||||
static NSColor* RGB(const uint32_t color)
|
||||
{
|
||||
return RGB(PalEntry(color));
|
||||
}
|
||||
|
||||
|
||||
static const CGFloat PROGRESS_BAR_HEIGHT = 18.0f;
|
||||
static const CGFloat NET_VIEW_HEIGHT = 88.0f;
|
||||
|
||||
|
||||
FConsoleWindow::FConsoleWindow()
|
||||
: m_window([NSWindow alloc])
|
||||
, m_textView([NSTextView alloc])
|
||||
, m_scrollView([NSScrollView alloc])
|
||||
, m_progressBar(nil)
|
||||
, m_netView(nil)
|
||||
, m_netMessageText(nil)
|
||||
, m_netCountText(nil)
|
||||
, m_netProgressBar(nil)
|
||||
, m_netAbortButton(nil)
|
||||
, m_characterCount(0)
|
||||
, m_netCurPos(0)
|
||||
, m_netMaxPos(0)
|
||||
{
|
||||
const CGFloat initialWidth = 512.0f;
|
||||
const CGFloat initialHeight = 384.0f;
|
||||
const NSRect initialRect = NSMakeRect(0.0f, 0.0f, initialWidth, initialHeight);
|
||||
|
||||
[m_textView initWithFrame:initialRect];
|
||||
[m_textView setEditable:NO];
|
||||
[m_textView setBackgroundColor:RGB(70, 70, 70)];
|
||||
[m_textView setMinSize:NSMakeSize(0.0f, initialHeight)];
|
||||
[m_textView setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
|
||||
[m_textView setVerticallyResizable:YES];
|
||||
[m_textView setHorizontallyResizable:NO];
|
||||
[m_textView setAutoresizingMask:NSViewWidthSizable];
|
||||
|
||||
NSTextContainer* const textContainer = [m_textView textContainer];
|
||||
[textContainer setContainerSize:NSMakeSize(initialWidth, FLT_MAX)];
|
||||
[textContainer setWidthTracksTextView:YES];
|
||||
|
||||
[m_scrollView initWithFrame:initialRect];
|
||||
[m_scrollView setBorderType:NSNoBorder];
|
||||
[m_scrollView setHasVerticalScroller:YES];
|
||||
[m_scrollView setHasHorizontalScroller:NO];
|
||||
[m_scrollView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
[m_scrollView setDocumentView:m_textView];
|
||||
|
||||
NSString* const title = [NSString stringWithFormat:@"%s %s - Console", GAMESIG, GetVersionString()];
|
||||
|
||||
[m_window initWithContentRect:initialRect
|
||||
styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
[m_window setMinSize:[m_window frame].size];
|
||||
[m_window setShowsResizeIndicator:NO];
|
||||
[m_window setTitle:title];
|
||||
[m_window center];
|
||||
[m_window exitAppOnClose];
|
||||
|
||||
// Do not allow fullscreen mode for this window
|
||||
[m_window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
|
||||
|
||||
[[m_window contentView] addSubview:m_scrollView];
|
||||
|
||||
[m_window makeKeyAndOrderFront:nil];
|
||||
}
|
||||
|
||||
|
||||
static FConsoleWindow* s_instance;
|
||||
|
||||
|
||||
void FConsoleWindow::CreateInstance()
|
||||
{
|
||||
assert(NULL == s_instance);
|
||||
s_instance = new FConsoleWindow;
|
||||
}
|
||||
|
||||
void FConsoleWindow::DeleteInstance()
|
||||
{
|
||||
assert(NULL != s_instance);
|
||||
delete s_instance;
|
||||
s_instance = NULL;
|
||||
}
|
||||
|
||||
FConsoleWindow& FConsoleWindow::GetInstance()
|
||||
{
|
||||
assert(NULL != s_instance);
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
|
||||
void FConsoleWindow::Show(const bool visible)
|
||||
{
|
||||
if (visible)
|
||||
{
|
||||
[m_window orderFront:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
[m_window orderOut:nil];
|
||||
}
|
||||
}
|
||||
|
||||
void FConsoleWindow::ShowFatalError(const char* const message)
|
||||
{
|
||||
SetProgressBar(false);
|
||||
NetDone();
|
||||
|
||||
const CGFloat textViewWidth = [m_scrollView frame].size.width;
|
||||
|
||||
ExpandTextView(-32.0f);
|
||||
|
||||
NSButton* quitButton = [[NSButton alloc] initWithFrame:NSMakeRect(textViewWidth - 76.0f, 0.0f, 72.0f, 30.0f)];
|
||||
[quitButton setAutoresizingMask:NSViewMinXMargin];
|
||||
[quitButton setBezelStyle:NSRoundedBezelStyle];
|
||||
[quitButton setTitle:@"Quit"];
|
||||
[quitButton setKeyEquivalent:@"\r"];
|
||||
[quitButton setTarget:NSApp];
|
||||
[quitButton setAction:@selector(stopModal)];
|
||||
|
||||
NSView* quitPanel = [[NSView alloc] initWithFrame:NSMakeRect(0.0f, 0.0f, textViewWidth, 32.0f)];
|
||||
[quitPanel setAutoresizingMask:NSViewWidthSizable];
|
||||
[quitPanel addSubview:quitButton];
|
||||
|
||||
[[m_window contentView] addSubview:quitPanel];
|
||||
[m_window orderFront:nil];
|
||||
|
||||
AddText(PalEntry(255, 0, 0), "\nExecution could not continue.\n");
|
||||
AddText(PalEntry(255, 255, 170), message);
|
||||
AddText("\n");
|
||||
|
||||
ScrollTextToBottom();
|
||||
|
||||
[NSApp runModalForWindow:m_window];
|
||||
}
|
||||
|
||||
|
||||
static const unsigned int THIRTY_FPS = 33; // milliseconds per update
|
||||
|
||||
|
||||
template <typename Function, unsigned int interval = THIRTY_FPS>
|
||||
struct TimedUpdater
|
||||
{
|
||||
explicit TimedUpdater(const Function& function)
|
||||
{
|
||||
const unsigned int currentTime = I_msTime();
|
||||
|
||||
if (currentTime - m_previousTime > interval)
|
||||
{
|
||||
m_previousTime = currentTime;
|
||||
|
||||
function();
|
||||
|
||||
[[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode];
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int m_previousTime;
|
||||
};
|
||||
|
||||
template <typename Function, unsigned int interval>
|
||||
unsigned int TimedUpdater<Function, interval>::m_previousTime;
|
||||
|
||||
template <typename Function, unsigned int interval = THIRTY_FPS>
|
||||
static void UpdateTimed(const Function& function)
|
||||
{
|
||||
TimedUpdater<Function, interval> dummy(function);
|
||||
}
|
||||
|
||||
|
||||
void FConsoleWindow::AddText(const char* message)
|
||||
{
|
||||
PalEntry color(223, 223, 223);
|
||||
|
||||
char buffer[1024] = {};
|
||||
size_t pos = 0;
|
||||
bool reset = false;
|
||||
|
||||
while (*message != '\0')
|
||||
{
|
||||
if ((TEXTCOLOR_ESCAPE == *message && 0 != pos)
|
||||
|| (pos == sizeof buffer - 1)
|
||||
|| reset)
|
||||
{
|
||||
buffer[pos] = '\0';
|
||||
pos = 0;
|
||||
reset = false;
|
||||
|
||||
AddText(color, buffer);
|
||||
}
|
||||
|
||||
if (TEXTCOLOR_ESCAPE == *message)
|
||||
{
|
||||
const uint8_t* colorID = reinterpret_cast<const uint8_t*>(message) + 1;
|
||||
if ('\0' == *colorID)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const EColorRange range = V_ParseFontColor(colorID, CR_UNTRANSLATED, CR_YELLOW);
|
||||
|
||||
if (range != CR_UNDEFINED)
|
||||
{
|
||||
color = V_LogColorFromColorRange(range);
|
||||
}
|
||||
|
||||
message += 2;
|
||||
}
|
||||
else if (0x1d == *message || 0x1f == *message) // Opening and closing bar characters
|
||||
{
|
||||
buffer[pos++] = '-';
|
||||
++message;
|
||||
}
|
||||
else if (0x1e == *message) // Middle bar character
|
||||
{
|
||||
buffer[pos++] = '=';
|
||||
++message;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[pos++] = *message++;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != pos)
|
||||
{
|
||||
buffer[pos] = '\0';
|
||||
|
||||
AddText(color, buffer);
|
||||
}
|
||||
|
||||
if ([m_window isVisible])
|
||||
{
|
||||
UpdateTimed([&]()
|
||||
{
|
||||
[m_textView scrollRangeToVisible:NSMakeRange(m_characterCount, 0)];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void FConsoleWindow::AddText(const PalEntry& color, const char* const message)
|
||||
{
|
||||
NSString* const text = [NSString stringWithCString:message
|
||||
encoding:NSISOLatin1StringEncoding];
|
||||
|
||||
NSDictionary* const attributes = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSFont systemFontOfSize:14.0f], NSFontAttributeName,
|
||||
RGB(color), NSForegroundColorAttributeName,
|
||||
nil];
|
||||
|
||||
NSAttributedString* const formattedText =
|
||||
[[NSAttributedString alloc] initWithString:text
|
||||
attributes:attributes];
|
||||
[[m_textView textStorage] appendAttributedString:formattedText];
|
||||
|
||||
m_characterCount += [text length];
|
||||
}
|
||||
|
||||
|
||||
void FConsoleWindow::ScrollTextToBottom()
|
||||
{
|
||||
[m_textView scrollRangeToVisible:NSMakeRange(m_characterCount, 0)];
|
||||
|
||||
[[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
|
||||
void FConsoleWindow::SetTitleText()
|
||||
{
|
||||
static const CGFloat TITLE_TEXT_HEIGHT = 32.0f;
|
||||
|
||||
NSRect textViewFrame = [m_scrollView frame];
|
||||
textViewFrame.size.height -= TITLE_TEXT_HEIGHT;
|
||||
[m_scrollView setFrame:textViewFrame];
|
||||
|
||||
const NSRect titleTextRect = NSMakeRect(
|
||||
0.0f,
|
||||
textViewFrame.origin.y + textViewFrame.size.height,
|
||||
textViewFrame.size.width,
|
||||
TITLE_TEXT_HEIGHT);
|
||||
|
||||
// Temporary solution for the same foreground and background colors
|
||||
// It's used in graphical startup screen, with Hexen style in particular
|
||||
// Native OS X backend doesn't implement this yet
|
||||
|
||||
if (DoomStartupInfo.FgColor == DoomStartupInfo.BkColor)
|
||||
{
|
||||
DoomStartupInfo.FgColor = ~DoomStartupInfo.FgColor;
|
||||
}
|
||||
|
||||
NSTextField* titleText = [[NSTextField alloc] initWithFrame:titleTextRect];
|
||||
[titleText setStringValue:[NSString stringWithCString:DoomStartupInfo.Name
|
||||
encoding:NSISOLatin1StringEncoding]];
|
||||
[titleText setAlignment:NSCenterTextAlignment];
|
||||
[titleText setTextColor:RGB(DoomStartupInfo.FgColor)];
|
||||
[titleText setBackgroundColor:RGB(DoomStartupInfo.BkColor)];
|
||||
[titleText setFont:[NSFont fontWithName:@"Trebuchet MS Bold" size:18.0f]];
|
||||
[titleText setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin];
|
||||
[titleText setSelectable:NO];
|
||||
[titleText setBordered:NO];
|
||||
|
||||
[[m_window contentView] addSubview:titleText];
|
||||
}
|
||||
|
||||
void FConsoleWindow::SetProgressBar(const bool visible)
|
||||
{
|
||||
if ( (!visible && nil == m_progressBar)
|
||||
|| (visible && nil != m_progressBar))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (visible)
|
||||
{
|
||||
ExpandTextView(-PROGRESS_BAR_HEIGHT);
|
||||
|
||||
static const CGFloat PROGRESS_BAR_X = 2.0f;
|
||||
const NSRect PROGRESS_BAR_RECT = NSMakeRect(
|
||||
PROGRESS_BAR_X, 0.0f,
|
||||
[m_window frame].size.width - PROGRESS_BAR_X * 2, 16.0f);
|
||||
|
||||
m_progressBar = [[NSProgressIndicator alloc] initWithFrame:PROGRESS_BAR_RECT];
|
||||
[m_progressBar setIndeterminate:NO];
|
||||
[m_progressBar setAutoresizingMask:NSViewWidthSizable];
|
||||
|
||||
[[m_window contentView] addSubview:m_progressBar];
|
||||
}
|
||||
else
|
||||
{
|
||||
ExpandTextView(PROGRESS_BAR_HEIGHT);
|
||||
|
||||
[m_progressBar removeFromSuperview];
|
||||
[m_progressBar release];
|
||||
m_progressBar = nil;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FConsoleWindow::ExpandTextView(const float height)
|
||||
{
|
||||
NSRect textFrame = [m_scrollView frame];
|
||||
textFrame.origin.y -= height;
|
||||
textFrame.size.height += height;
|
||||
[m_scrollView setFrame:textFrame];
|
||||
}
|
||||
|
||||
|
||||
void FConsoleWindow::Progress(const int current, const int maximum)
|
||||
{
|
||||
if (nil == m_progressBar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateTimed([&]()
|
||||
{
|
||||
[m_progressBar setMaxValue:maximum];
|
||||
[m_progressBar setDoubleValue:current];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void FConsoleWindow::NetInit(const char* const message, const int playerCount)
|
||||
{
|
||||
if (nil == m_netView)
|
||||
{
|
||||
SetProgressBar(false);
|
||||
ExpandTextView(-NET_VIEW_HEIGHT);
|
||||
|
||||
// Message like 'Waiting for players' or 'Contacting host'
|
||||
m_netMessageText = [[NSTextField alloc] initWithFrame:NSMakeRect(12.0f, 64.0f, 400.0f, 16.0f)];
|
||||
[m_netMessageText setAutoresizingMask:NSViewWidthSizable];
|
||||
[m_netMessageText setDrawsBackground:NO];
|
||||
[m_netMessageText setSelectable:NO];
|
||||
[m_netMessageText setBordered:NO];
|
||||
|
||||
// Text with connected/total players count
|
||||
m_netCountText = [[NSTextField alloc] initWithFrame:NSMakeRect(428.0f, 64.0f, 72.0f, 16.0f)];
|
||||
[m_netCountText setAutoresizingMask:NSViewMinXMargin];
|
||||
[m_netCountText setAlignment:NSRightTextAlignment];
|
||||
[m_netCountText setDrawsBackground:NO];
|
||||
[m_netCountText setSelectable:NO];
|
||||
[m_netCountText setBordered:NO];
|
||||
|
||||
// Connection progress
|
||||
m_netProgressBar = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(12.0f, 40.0f, 488.0f, 16.0f)];
|
||||
[m_netProgressBar setAutoresizingMask:NSViewWidthSizable];
|
||||
[m_netProgressBar setMaxValue:playerCount];
|
||||
|
||||
if (0 == playerCount)
|
||||
{
|
||||
// Joining game
|
||||
[m_netProgressBar setIndeterminate:YES];
|
||||
[m_netProgressBar startAnimation:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hosting game
|
||||
[m_netProgressBar setIndeterminate:NO];
|
||||
}
|
||||
|
||||
// Cancel network game button
|
||||
m_netAbortButton = [[NSButton alloc] initWithFrame:NSMakeRect(432.0f, 8.0f, 72.0f, 28.0f)];
|
||||
[m_netAbortButton setAutoresizingMask:NSViewMinXMargin];
|
||||
[m_netAbortButton setBezelStyle:NSRoundedBezelStyle];
|
||||
[m_netAbortButton setTitle:@"Cancel"];
|
||||
[m_netAbortButton setKeyEquivalent:@"\r"];
|
||||
[m_netAbortButton setTarget:[NSApp delegate]];
|
||||
[m_netAbortButton setAction:@selector(sendExitEvent:)];
|
||||
|
||||
// Panel for controls above
|
||||
m_netView = [[NSView alloc] initWithFrame:NSMakeRect(0.0f, 0.0f, 512.0f, NET_VIEW_HEIGHT)];
|
||||
[m_netView setAutoresizingMask:NSViewWidthSizable];
|
||||
[m_netView addSubview:m_netMessageText];
|
||||
[m_netView addSubview:m_netCountText];
|
||||
[m_netView addSubview:m_netProgressBar];
|
||||
[m_netView addSubview:m_netAbortButton];
|
||||
|
||||
NSRect windowRect = [m_window frame];
|
||||
windowRect.origin.y -= NET_VIEW_HEIGHT;
|
||||
windowRect.size.height += NET_VIEW_HEIGHT;
|
||||
|
||||
[m_window setFrame:windowRect display:YES];
|
||||
[[m_window contentView] addSubview:m_netView];
|
||||
|
||||
ScrollTextToBottom();
|
||||
}
|
||||
|
||||
[m_netMessageText setStringValue:[NSString stringWithUTF8String:message]];
|
||||
|
||||
m_netCurPos = 0;
|
||||
m_netMaxPos = playerCount;
|
||||
|
||||
NetProgress(1); // You always know about yourself
|
||||
}
|
||||
|
||||
void FConsoleWindow::NetProgress(const int count)
|
||||
{
|
||||
if (0 == count)
|
||||
{
|
||||
++m_netCurPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_netCurPos = count;
|
||||
}
|
||||
|
||||
if (nil == m_netView)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_netMaxPos > 1)
|
||||
{
|
||||
[m_netCountText setStringValue:[NSString stringWithFormat:@"%d / %d", m_netCurPos, m_netMaxPos]];
|
||||
[m_netProgressBar setDoubleValue:MIN(m_netCurPos, m_netMaxPos)];
|
||||
}
|
||||
}
|
||||
|
||||
void FConsoleWindow::NetDone()
|
||||
{
|
||||
if (nil != m_netView)
|
||||
{
|
||||
ExpandTextView(NET_VIEW_HEIGHT);
|
||||
|
||||
[m_netView removeFromSuperview];
|
||||
[m_netView release];
|
||||
m_netView = nil;
|
||||
|
||||
// Released by m_netView
|
||||
m_netMessageText = nil;
|
||||
m_netCountText = nil;
|
||||
m_netProgressBar = nil;
|
||||
m_netAbortButton = nil;
|
||||
}
|
||||
}
|
176
source/platform/posix/cocoa/st_start.mm
Normal file
176
source/platform/posix/cocoa/st_start.mm
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
** st_start.mm
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2015 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 <unistd.h>
|
||||
|
||||
#import <Foundation/NSRunLoop.h>
|
||||
|
||||
#include "c_cvars.h"
|
||||
#include "doomtype.h"
|
||||
#include "st_console.h"
|
||||
#include "st_start.h"
|
||||
#include "doomerrors.h"
|
||||
|
||||
|
||||
FStartupScreen *StartScreen;
|
||||
|
||||
|
||||
CUSTOM_CVAR(Int, showendoom, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (self < 0)
|
||||
{
|
||||
self = 0;
|
||||
}
|
||||
else if (self > 2)
|
||||
{
|
||||
self = 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
FBasicStartupScreen::FBasicStartupScreen(int maxProgress, bool showBar)
|
||||
: FStartupScreen(maxProgress)
|
||||
{
|
||||
FConsoleWindow& consoleWindow = FConsoleWindow::GetInstance();
|
||||
consoleWindow.SetProgressBar(true);
|
||||
consoleWindow.SetTitleText();
|
||||
|
||||
#if 0
|
||||
// Testing code, please do not remove
|
||||
consoleWindow.AddText("----------------------------------------------------------------\n");
|
||||
consoleWindow.AddText("1234567890 !@#$%^&*() ,<.>/?;:'\" [{]}\\| `~-_=+ "
|
||||
"This is very very very long message needed to trigger word wrapping...\n\n");
|
||||
consoleWindow.AddText("Multiline...\n\tmessage...\n\t\twith...\n\t\t\ttabs.\n\n");
|
||||
|
||||
consoleWindow.AddText(TEXTCOLOR_BRICK "TEXTCOLOR_BRICK\n" TEXTCOLOR_TAN "TEXTCOLOR_TAN\n");
|
||||
consoleWindow.AddText(TEXTCOLOR_GRAY "TEXTCOLOR_GRAY & TEXTCOLOR_GREY\n");
|
||||
consoleWindow.AddText(TEXTCOLOR_GREEN "TEXTCOLOR_GREEN\n" TEXTCOLOR_BROWN "TEXTCOLOR_BROWN\n");
|
||||
consoleWindow.AddText(TEXTCOLOR_GOLD "TEXTCOLOR_GOLD\n" TEXTCOLOR_RED "TEXTCOLOR_RED\n");
|
||||
consoleWindow.AddText(TEXTCOLOR_BLUE "TEXTCOLOR_BLUE\n" TEXTCOLOR_ORANGE "TEXTCOLOR_ORANGE\n");
|
||||
consoleWindow.AddText(TEXTCOLOR_WHITE "TEXTCOLOR_WHITE\n" TEXTCOLOR_YELLOW "TEXTCOLOR_YELLOW\n");
|
||||
consoleWindow.AddText(TEXTCOLOR_UNTRANSLATED "TEXTCOLOR_UNTRANSLATED\n");
|
||||
consoleWindow.AddText(TEXTCOLOR_BLACK "TEXTCOLOR_BLACK\n" TEXTCOLOR_LIGHTBLUE "TEXTCOLOR_LIGHTBLUE\n");
|
||||
consoleWindow.AddText(TEXTCOLOR_CREAM "TEXTCOLOR_CREAM\n" TEXTCOLOR_OLIVE "TEXTCOLOR_OLIVE\n");
|
||||
consoleWindow.AddText(TEXTCOLOR_DARKGREEN "TEXTCOLOR_DARKGREEN\n" TEXTCOLOR_DARKRED "TEXTCOLOR_DARKRED\n");
|
||||
consoleWindow.AddText(TEXTCOLOR_DARKBROWN "TEXTCOLOR_DARKBROWN\n" TEXTCOLOR_PURPLE "TEXTCOLOR_PURPLE\n");
|
||||
consoleWindow.AddText(TEXTCOLOR_DARKGRAY "TEXTCOLOR_DARKGRAY\n" TEXTCOLOR_CYAN "TEXTCOLOR_CYAN\n");
|
||||
consoleWindow.AddText(TEXTCOLOR_ICE "TEXTCOLOR_ICE\n" TEXTCOLOR_FIRE "TEXTCOLOR_FIRE\n");
|
||||
consoleWindow.AddText(TEXTCOLOR_SAPPHIRE "TEXTCOLOR_SAPPHIRE\n" TEXTCOLOR_TEAL "TEXTCOLOR_TEAL\n");
|
||||
consoleWindow.AddText(TEXTCOLOR_NORMAL "TEXTCOLOR_NORMAL\n" TEXTCOLOR_BOLD "TEXTCOLOR_BOLD\n");
|
||||
consoleWindow.AddText(TEXTCOLOR_CHAT "TEXTCOLOR_CHAT\n" TEXTCOLOR_TEAMCHAT "TEXTCOLOR_TEAMCHAT\n");
|
||||
consoleWindow.AddText("----------------------------------------------------------------\n");
|
||||
#endif // _DEBUG
|
||||
}
|
||||
|
||||
FBasicStartupScreen::~FBasicStartupScreen()
|
||||
{
|
||||
FConsoleWindow::GetInstance().SetProgressBar(false);
|
||||
}
|
||||
|
||||
|
||||
void FBasicStartupScreen::Progress()
|
||||
{
|
||||
if (CurPos < MaxPos)
|
||||
{
|
||||
++CurPos;
|
||||
}
|
||||
|
||||
FConsoleWindow::GetInstance().Progress(CurPos, MaxPos);
|
||||
}
|
||||
|
||||
|
||||
void FBasicStartupScreen::NetInit(const char* const message, const int playerCount)
|
||||
{
|
||||
FConsoleWindow::GetInstance().NetInit(message, playerCount);
|
||||
}
|
||||
|
||||
void FBasicStartupScreen::NetProgress(const int count)
|
||||
{
|
||||
FConsoleWindow::GetInstance().NetProgress(count);
|
||||
}
|
||||
|
||||
void FBasicStartupScreen::NetMessage(const char* const format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
FString message;
|
||||
message.VFormat(format, args);
|
||||
va_end(args);
|
||||
|
||||
Printf("%s\n", message.GetChars());
|
||||
}
|
||||
|
||||
void FBasicStartupScreen::NetDone()
|
||||
{
|
||||
FConsoleWindow::GetInstance().NetDone();
|
||||
}
|
||||
|
||||
bool FBasicStartupScreen::NetLoop(bool (*timerCallback)(void*), void* const userData)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (timerCallback(userData))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
[[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode];
|
||||
|
||||
// Do not poll to often
|
||||
usleep(50000);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
FStartupScreen *FStartupScreen::CreateInstance(const int maxProgress)
|
||||
{
|
||||
return new FBasicStartupScreen(maxProgress, true);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
void ST_Endoom()
|
||||
{
|
||||
throw CExitEvent(0);
|
||||
}
|
155
source/platform/posix/dikeys.h
Normal file
155
source/platform/posix/dikeys.h
Normal file
|
@ -0,0 +1,155 @@
|
|||
// ZDoom bases its keycodes on DirectInput's scan codes
|
||||
// Why? Because it was Win32-only before porting to anything else,
|
||||
// so this made sense. AFAIK, it's primarily used under Win32 now,
|
||||
// so it still makes sense.
|
||||
//
|
||||
// Actually, these key codes may only be used for key bindings now,
|
||||
// in which case they're not really necessary--if we tweaked c_bind.cpp.
|
||||
|
||||
enum
|
||||
{
|
||||
DIK_ESCAPE = 1,
|
||||
DIK_1,
|
||||
DIK_2,
|
||||
DIK_3,
|
||||
DIK_4,
|
||||
DIK_5,
|
||||
DIK_6,
|
||||
DIK_7,
|
||||
DIK_8,
|
||||
DIK_9,
|
||||
DIK_0,
|
||||
DIK_MINUS, /* - on main keyboard */
|
||||
DIK_EQUALS,
|
||||
DIK_BACK, /* backspace */
|
||||
DIK_TAB,
|
||||
DIK_Q,
|
||||
DIK_W,
|
||||
DIK_E,
|
||||
DIK_R,
|
||||
DIK_T,
|
||||
DIK_Y,
|
||||
DIK_U,
|
||||
DIK_I,
|
||||
DIK_O,
|
||||
DIK_P,
|
||||
DIK_LBRACKET,
|
||||
DIK_RBRACKET,
|
||||
DIK_RETURN, /* Enter on main keyboard */
|
||||
DIK_LCONTROL,
|
||||
DIK_A,
|
||||
DIK_S,
|
||||
DIK_D,
|
||||
DIK_F,
|
||||
DIK_G,
|
||||
DIK_H,
|
||||
DIK_J,
|
||||
DIK_K,
|
||||
DIK_L,
|
||||
DIK_SEMICOLON,
|
||||
DIK_APOSTROPHE,
|
||||
DIK_GRAVE, /* accent grave */
|
||||
DIK_LSHIFT,
|
||||
DIK_BACKSLASH,
|
||||
DIK_Z,
|
||||
DIK_X,
|
||||
DIK_C,
|
||||
DIK_V,
|
||||
DIK_B,
|
||||
DIK_N,
|
||||
DIK_M,
|
||||
DIK_COMMA,
|
||||
DIK_PERIOD, /* . on main keyboard */
|
||||
DIK_SLASH, /* / on main keyboard */
|
||||
DIK_RSHIFT,
|
||||
DIK_MULTIPLY, /* * on numeric keypad */
|
||||
DIK_LMENU, /* left Alt */
|
||||
DIK_SPACE,
|
||||
DIK_CAPITAL,
|
||||
DIK_F1,
|
||||
DIK_F2,
|
||||
DIK_F3,
|
||||
DIK_F4,
|
||||
DIK_F5,
|
||||
DIK_F6,
|
||||
DIK_F7,
|
||||
DIK_F8,
|
||||
DIK_F9,
|
||||
DIK_F10,
|
||||
DIK_NUMLOCK,
|
||||
DIK_SCROLL, /* Scroll Lock */
|
||||
DIK_NUMPAD7,
|
||||
DIK_NUMPAD8,
|
||||
DIK_NUMPAD9,
|
||||
DIK_SUBTRACT, /* - on numeric keypad */
|
||||
DIK_NUMPAD4,
|
||||
DIK_NUMPAD5,
|
||||
DIK_NUMPAD6,
|
||||
DIK_ADD, /* + on numeric keypad */
|
||||
DIK_NUMPAD1,
|
||||
DIK_NUMPAD2,
|
||||
DIK_NUMPAD3,
|
||||
DIK_NUMPAD0,
|
||||
DIK_DECIMAL, /* . on numeric keypad */
|
||||
DIK_OEM_102 = 0x56, /* < > | on UK/Germany keyboards */
|
||||
DIK_F11,
|
||||
DIK_F12,
|
||||
DIK_F13 = 0x64, /* (NEC PC98) */
|
||||
DIK_F14, /* (NEC PC98) */
|
||||
DIK_F15, /* (NEC PC98) */
|
||||
DIK_KANA = 0x70, /* (Japanese keyboard) */
|
||||
DIK_ABNT_C1 = 0x73, /* / ? on Portugese (Brazilian) keyboards */
|
||||
DIK_CONVERT = 0x79, /* (Japanese keyboard) */
|
||||
DIK_NOCONVERT = 0x7B, /* (Japanese keyboard) */
|
||||
DIK_YEN = 0x7D, /* (Japanese keyboard) */
|
||||
DIK_ABNT_C2 = 0x7E, /* Numpad . on Portugese (Brazilian) keyboards */
|
||||
DIK_NUMPAD_EQUALS = 0x8D, /* = on numeric keypad (NEC PC98) */
|
||||
DIK_PREVTRACK = 0x90, /* Previous Track (DIK_CIRCUMFLEX on Japanese keyboard) */
|
||||
DIK_AT, /* (NEC PC98) */
|
||||
DIK_COLON, /* (NEC PC98) */
|
||||
DIK_UNDERLINE, /* (NEC PC98) */
|
||||
DIK_KANJI, /* (Japanese keyboard) */
|
||||
DIK_STOP, /* (NEC PC98) */
|
||||
DIK_AX, /* (Japan AX) */
|
||||
DIK_UNLABELED, /* (J3100) */
|
||||
DIK_NEXTTRACK = 0x99, /* Next Track */
|
||||
DIK_NUMPADENTER = 0x9C, /* Enter on numeric keypad */
|
||||
DIK_RCONTROL = 0x9D,
|
||||
DIK_MUTE = 0xA0, /* Mute */
|
||||
DIK_CALCULATOR = 0xA1, /* Calculator */
|
||||
DIK_PLAYPAUSE = 0xA2, /* Play / Pause */
|
||||
DIK_MEDIASTOP = 0xA4, /* Media Stop */
|
||||
DIK_VOLUMEDOWN = 0xAE, /* Volume - */
|
||||
DIK_VOLUMEUP = 0xB0, /* Volume + */
|
||||
DIK_WEBHOME = 0xB2, /* Web home */
|
||||
DIK_NUMPADCOMMA = 0xB3, /* , on numeric keypad (NEC PC98) */
|
||||
DIK_DIVIDE = 0xB5, /* / on numeric keypad */
|
||||
DIK_SYSRQ = 0xB7,
|
||||
DIK_RMENU = 0xB8, /* right Alt */
|
||||
DIK_PAUSE = 0xC5, /* Pause */
|
||||
DIK_HOME = 0xC7, /* Home on arrow keypad */
|
||||
DIK_UP = 0xC8, /* UpArrow on arrow keypad */
|
||||
DIK_PRIOR = 0xC9, /* PgUp on arrow keypad */
|
||||
DIK_LEFT = 0xCB, /* LeftArrow on arrow keypad */
|
||||
DIK_RIGHT = 0xCD, /* RightArrow on arrow keypad */
|
||||
DIK_END = 0xCF, /* End on arrow keypad */
|
||||
DIK_DOWN = 0xD0, /* DownArrow on arrow keypad */
|
||||
DIK_NEXT = 0xD1, /* PgDn on arrow keypad */
|
||||
DIK_INSERT = 0xD2, /* Insert on arrow keypad */
|
||||
DIK_DELETE = 0xD3, /* Delete on arrow keypad */
|
||||
DIK_LWIN = 0xDB, /* Left Windows key */
|
||||
DIK_RWIN = 0xDC, /* Right Windows key */
|
||||
DIK_APPS = 0xDD, /* AppMenu key */
|
||||
DIK_POWER = 0xDE, /* System Power */
|
||||
DIK_SLEEP = 0xDF, /* System Sleep */
|
||||
DIK_WAKE = 0xE3, /* System Wake */
|
||||
DIK_WEBSEARCH = 0xE5, /* Web Search */
|
||||
DIK_WEBFAVORITES = 0xE6, /* Web Favorites */
|
||||
DIK_WEBREFRESH = 0xE7, /* Web Refresh */
|
||||
DIK_WEBSTOP = 0xE8, /* Web Stop */
|
||||
DIK_WEBFORWARD = 0xE9, /* Web Forward */
|
||||
DIK_WEBBACK = 0xEA, /* Web Back */
|
||||
DIK_MYCOMPUTER = 0xEB, /* My Computer */
|
||||
DIK_MAIL = 0xEC, /* Mail */
|
||||
DIK_MEDIASELECT = 0xED /* Media Select */
|
||||
};
|
40
source/platform/posix/hardware.h
Normal file
40
source/platform/posix/hardware.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
** hardware.h
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2006 Randy Heit
|
||||
** 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef __HARDWARE_H__
|
||||
#define __HARDWARE_H__
|
||||
|
||||
#include "i_video.h"
|
||||
#include "v_video.h"
|
||||
|
||||
#endif // __HARDWARE_H__
|
227
source/platform/posix/i_steam.cpp
Normal file
227
source/platform/posix/i_steam.cpp
Normal file
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
** i_steam.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2013 Braden Obrzut
|
||||
** 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/stat.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "m_misc.h"
|
||||
#endif // __APPLE__
|
||||
|
||||
#include "doomerrors.h"
|
||||
#include "d_main.h"
|
||||
#include "sc_man.h"
|
||||
#include "cmdlib.h"
|
||||
|
||||
static void PSR_FindEndBlock(FScanner &sc)
|
||||
{
|
||||
int depth = 1;
|
||||
do
|
||||
{
|
||||
if(sc.CheckToken('}'))
|
||||
--depth;
|
||||
else if(sc.CheckToken('{'))
|
||||
++depth;
|
||||
else
|
||||
sc.MustGetAnyToken();
|
||||
}
|
||||
while(depth);
|
||||
}
|
||||
static void PSR_SkipBlock(FScanner &sc)
|
||||
{
|
||||
sc.MustGetToken('{');
|
||||
PSR_FindEndBlock(sc);
|
||||
}
|
||||
static bool PSR_FindAndEnterBlock(FScanner &sc, const char* keyword)
|
||||
{
|
||||
// Finds a block with a given keyword and then enter it (opening brace)
|
||||
// Should be closed with PSR_FindEndBlock
|
||||
while(sc.GetToken())
|
||||
{
|
||||
if(sc.TokenType == '}')
|
||||
{
|
||||
sc.UnGet();
|
||||
return false;
|
||||
}
|
||||
|
||||
sc.TokenMustBe(TK_StringConst);
|
||||
if(!sc.Compare(keyword))
|
||||
{
|
||||
if(!sc.CheckToken(TK_StringConst))
|
||||
PSR_SkipBlock(sc);
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.MustGetToken('{');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static TArray<FString> PSR_ReadBaseInstalls(FScanner &sc)
|
||||
{
|
||||
TArray<FString> result;
|
||||
|
||||
// Get a list of possible install directories.
|
||||
while(sc.GetToken())
|
||||
{
|
||||
if(sc.TokenType == '}')
|
||||
break;
|
||||
|
||||
sc.TokenMustBe(TK_StringConst);
|
||||
FString key(sc.String);
|
||||
if(key.Left(18).CompareNoCase("BaseInstallFolder_") == 0)
|
||||
{
|
||||
sc.MustGetToken(TK_StringConst);
|
||||
result.Push(FString(sc.String) + "/steamapps/common");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(sc.CheckToken('{'))
|
||||
PSR_FindEndBlock(sc);
|
||||
else
|
||||
sc.MustGetToken(TK_StringConst);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
static TArray<FString> ParseSteamRegistry(const char* path)
|
||||
{
|
||||
TArray<FString> dirs;
|
||||
|
||||
// Read registry data
|
||||
FScanner sc;
|
||||
if (sc.OpenFile(path))
|
||||
{
|
||||
sc.SetCMode(true);
|
||||
|
||||
// Find the SteamApps listing
|
||||
if (PSR_FindAndEnterBlock(sc, "InstallConfigStore"))
|
||||
{
|
||||
if (PSR_FindAndEnterBlock(sc, "Software"))
|
||||
{
|
||||
if (PSR_FindAndEnterBlock(sc, "Valve"))
|
||||
{
|
||||
if (PSR_FindAndEnterBlock(sc, "Steam"))
|
||||
{
|
||||
dirs = PSR_ReadBaseInstalls(sc);
|
||||
}
|
||||
PSR_FindEndBlock(sc);
|
||||
}
|
||||
PSR_FindEndBlock(sc);
|
||||
}
|
||||
PSR_FindEndBlock(sc);
|
||||
}
|
||||
}
|
||||
return dirs;
|
||||
}
|
||||
|
||||
static struct SteamAppInfo
|
||||
{
|
||||
const char* const BasePath;
|
||||
const int AppID;
|
||||
} AppInfo[] =
|
||||
{
|
||||
{"Doom 2/base", 2300},
|
||||
{"Final Doom/base", 2290},
|
||||
{"Heretic Shadow of the Serpent Riders/base", 2390},
|
||||
{"Hexen/base", 2360},
|
||||
{"Hexen Deathkings of the Dark Citadel/base", 2370},
|
||||
{"Ultimate Doom/base", 2280},
|
||||
{"DOOM 3 BFG Edition/base/wads", 208200},
|
||||
{"Strife", 317040}
|
||||
};
|
||||
|
||||
TArray<FString> I_GetSteamPath()
|
||||
{
|
||||
TArray<FString> result;
|
||||
TArray<FString> SteamInstallFolders;
|
||||
|
||||
// Linux and OS X actually allow the user to install to any location, so
|
||||
// we need to figure out on an app-by-app basis where the game is installed.
|
||||
// To do so, we read the virtual registry.
|
||||
#ifdef __APPLE__
|
||||
const FString appSupportPath = M_GetMacAppSupportPath();
|
||||
FString regPath = appSupportPath + "/Steam/config/config.vdf";
|
||||
try
|
||||
{
|
||||
SteamInstallFolders = ParseSteamRegistry(regPath);
|
||||
}
|
||||
catch(class CRecoverableError &error)
|
||||
{
|
||||
// If we can't parse for some reason just pretend we can't find anything.
|
||||
return result;
|
||||
}
|
||||
|
||||
SteamInstallFolders.Push(appSupportPath + "/Steam/SteamApps/common");
|
||||
#else
|
||||
char* home = getenv("HOME");
|
||||
if(home != NULL && *home != '\0')
|
||||
{
|
||||
FString regPath;
|
||||
regPath.Format("%s/.steam/config/config.vdf", home);
|
||||
// [BL] The config seems to have moved from the more modern .local to
|
||||
// .steam at some point. Not sure if it's just my setup so I guess we
|
||||
// can fall back on it?
|
||||
if(!FileExists(regPath))
|
||||
regPath.Format("%s/.local/share/Steam/config/config.vdf", home);
|
||||
|
||||
try
|
||||
{
|
||||
SteamInstallFolders = ParseSteamRegistry(regPath);
|
||||
}
|
||||
catch(class CRecoverableError &error)
|
||||
{
|
||||
// If we can't parse for some reason just pretend we can't find anything.
|
||||
return result;
|
||||
}
|
||||
|
||||
regPath.Format("%s/.local/share/Steam/SteamApps/common", home);
|
||||
SteamInstallFolders.Push(regPath);
|
||||
}
|
||||
#endif
|
||||
|
||||
for(unsigned int i = 0;i < SteamInstallFolders.Size();++i)
|
||||
{
|
||||
for(unsigned int app = 0;app < countof(AppInfo);++app)
|
||||
{
|
||||
struct stat st;
|
||||
FString candidate(SteamInstallFolders[i] + "/" + AppInfo[app].BasePath);
|
||||
if(DirExists(candidate))
|
||||
result.Push(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
116
source/platform/posix/i_system.h
Normal file
116
source/platform/posix/i_system.h
Normal file
|
@ -0,0 +1,116 @@
|
|||
#ifndef __I_SYSTEM__
|
||||
#define __I_SYSTEM__
|
||||
|
||||
#include <dirent.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#if defined(__sun) || defined(__sun__) || defined(__SRV4) || defined(__srv4__)
|
||||
#define __solaris__ 1
|
||||
#endif
|
||||
|
||||
#include "doomtype.h"
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
|
||||
struct ticcmd_t;
|
||||
struct WadStuff;
|
||||
|
||||
#ifndef SHARE_DIR
|
||||
#define SHARE_DIR "/usr/local/share/"
|
||||
#endif
|
||||
|
||||
|
||||
// Called by DoomMain.
|
||||
void I_Init (void);
|
||||
|
||||
// Return a seed value for the RNG.
|
||||
unsigned int I_MakeRNGSeed();
|
||||
|
||||
|
||||
void I_StartFrame (void);
|
||||
|
||||
void I_StartTic (void);
|
||||
|
||||
// Asynchronous interrupt functions should maintain private queues
|
||||
// that are read by the synchronous functions
|
||||
// to be converted into events.
|
||||
|
||||
// Either returns a null ticcmd,
|
||||
// or calls a loadable driver to build it.
|
||||
// This ticcmd will then be modified by the gameloop
|
||||
// for normal input.
|
||||
ticcmd_t *I_BaseTiccmd (void);
|
||||
|
||||
void I_Tactile (int on, int off, int total);
|
||||
|
||||
void I_DebugPrint (const char *cp);
|
||||
|
||||
// Print a console string
|
||||
void I_PrintStr (const char *str);
|
||||
|
||||
// Set the title string of the startup window
|
||||
void I_SetIWADInfo ();
|
||||
|
||||
// Pick from multiple IWADs to use
|
||||
int I_PickIWad (WadStuff *wads, int numwads, bool queryiwad, int defaultiwad);
|
||||
|
||||
// [RH] Checks the registry for Steam's install path, so we can scan its
|
||||
// directories for IWADs if the user purchased any through Steam.
|
||||
TArray<FString> I_GetSteamPath();
|
||||
|
||||
TArray<FString> I_GetGogPaths();
|
||||
|
||||
// The ini could not be saved at exit
|
||||
bool I_WriteIniFailed ();
|
||||
|
||||
class FTexture;
|
||||
bool I_SetCursor(FTexture *);
|
||||
|
||||
// Directory searching routines
|
||||
|
||||
struct findstate_t
|
||||
{
|
||||
private:
|
||||
int count;
|
||||
struct dirent **namelist;
|
||||
int current;
|
||||
|
||||
friend void *I_FindFirst(const char *filespec, findstate_t *fileinfo);
|
||||
friend int I_FindNext(void *handle, findstate_t *fileinfo);
|
||||
friend const char *I_FindName(findstate_t *fileinfo);
|
||||
friend int I_FindAttr(findstate_t *fileinfo);
|
||||
friend int I_FindClose(void *handle);
|
||||
};
|
||||
|
||||
void *I_FindFirst (const char *filespec, findstate_t *fileinfo);
|
||||
int I_FindNext (void *handle, findstate_t *fileinfo);
|
||||
int I_FindClose (void *handle);
|
||||
int I_FindAttr (findstate_t *fileinfo);
|
||||
|
||||
inline const char *I_FindName(findstate_t *fileinfo)
|
||||
{
|
||||
return (fileinfo->namelist[fileinfo->current]->d_name);
|
||||
}
|
||||
|
||||
#define FA_RDONLY 1
|
||||
#define FA_HIDDEN 2
|
||||
#define FA_SYSTEM 4
|
||||
#define FA_DIREC 8
|
||||
#define FA_ARCH 16
|
||||
|
||||
static inline char *strlwr(char *str)
|
||||
{
|
||||
char *ptr = str;
|
||||
while(*ptr)
|
||||
{
|
||||
*ptr = tolower(*ptr);
|
||||
++ptr;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
inline int I_GetNumaNodeCount() { return 1; }
|
||||
inline int I_GetNumaNodeThreadCount(int numaNode) { return std::max<int>(std::thread::hardware_concurrency(), 1); }
|
||||
inline void I_SetThreadNumaNode(std::thread &thread, int numaNode) { }
|
||||
|
||||
#endif
|
470
source/platform/posix/osx/iwadpicker_cocoa.mm
Normal file
470
source/platform/posix/osx/iwadpicker_cocoa.mm
Normal file
|
@ -0,0 +1,470 @@
|
|||
/*
|
||||
** iwadpicker_cocoa.mm
|
||||
**
|
||||
** Implements Mac OS X native IWAD Picker.
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2010 Braden Obrzut
|
||||
** 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 "cmdlib.h"
|
||||
#include "d_main.h"
|
||||
#include "version.h"
|
||||
#include "c_cvars.h"
|
||||
#include "m_argv.h"
|
||||
#include "m_misc.h"
|
||||
#include "gameconfigfile.h"
|
||||
#include "doomerrors.h"
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <wordexp.h>
|
||||
|
||||
|
||||
CVAR(String, osx_additional_parameters, "", CVAR_ARCHIVE | CVAR_NOSET | CVAR_GLOBALCONFIG);
|
||||
|
||||
enum
|
||||
{
|
||||
COLUMN_IWAD,
|
||||
COLUMN_GAME,
|
||||
|
||||
NUM_COLUMNS
|
||||
};
|
||||
|
||||
static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" };
|
||||
|
||||
// Class to convert the IWAD data into a form that Cocoa can use.
|
||||
@interface IWADTableData : NSObject<NSTableViewDataSource>
|
||||
{
|
||||
NSMutableArray *data;
|
||||
}
|
||||
|
||||
- (void)dealloc;
|
||||
- (IWADTableData *)init:(WadStuff *) wads num:(int) numwads;
|
||||
|
||||
- (int)numberOfRowsInTableView:(NSTableView *)aTableView;
|
||||
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex;
|
||||
@end
|
||||
|
||||
@implementation IWADTableData
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[data release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (IWADTableData *)init:(WadStuff *) wads num:(int) numwads
|
||||
{
|
||||
data = [[NSMutableArray alloc] initWithCapacity:numwads];
|
||||
|
||||
for(int i = 0;i < numwads;i++)
|
||||
{
|
||||
NSMutableDictionary *record = [[NSMutableDictionary alloc] initWithCapacity:NUM_COLUMNS];
|
||||
const char* filename = strrchr(wads[i].Path, '/');
|
||||
if(filename == NULL)
|
||||
filename = wads[i].Path;
|
||||
else
|
||||
filename++;
|
||||
[record setObject:[NSString stringWithUTF8String:filename] forKey:[NSString stringWithUTF8String:tableHeaders[COLUMN_IWAD]]];
|
||||
[record setObject:[NSString stringWithUTF8String:wads[i].Name] forKey:[NSString stringWithUTF8String:tableHeaders[COLUMN_GAME]]];
|
||||
[data addObject:record];
|
||||
[record release];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (int)numberOfRowsInTableView:(NSTableView *)aTableView
|
||||
{
|
||||
return [data count];
|
||||
}
|
||||
|
||||
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
|
||||
{
|
||||
NSParameterAssert(rowIndex >= 0 && (unsigned int) rowIndex < [data count]);
|
||||
NSMutableDictionary *record = [data objectAtIndex:rowIndex];
|
||||
return [record objectForKey:[aTableColumn identifier]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static NSDictionary* GetKnownFileTypes()
|
||||
{
|
||||
return [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"-file" , @"wad",
|
||||
@"-file" , @"pk3",
|
||||
@"-file" , @"zip",
|
||||
@"-file" , @"pk7",
|
||||
@"-file" , @"7z",
|
||||
@"-deh" , @"deh",
|
||||
@"-bex" , @"bex",
|
||||
@"-exec" , @"cfg",
|
||||
@"-playdemo", @"lmp",
|
||||
nil];
|
||||
}
|
||||
|
||||
static NSArray* GetKnownExtensions()
|
||||
{
|
||||
return [GetKnownFileTypes() allKeys];
|
||||
}
|
||||
|
||||
@interface NSMutableString(AppendKnownFileType)
|
||||
- (void)appendKnownFileType:(NSString *)filePath;
|
||||
@end
|
||||
|
||||
@implementation NSMutableString(AppendKnownFileType)
|
||||
- (void)appendKnownFileType:(NSString *)filePath
|
||||
{
|
||||
NSString* extension = [[filePath pathExtension] lowercaseString];
|
||||
NSString* parameter = [GetKnownFileTypes() objectForKey:extension];
|
||||
|
||||
if (nil == parameter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
[self appendFormat:@"%@ \"%@\" ", parameter, filePath];
|
||||
}
|
||||
@end
|
||||
|
||||
// So we can listen for button actions and such we need to have an Obj-C class.
|
||||
@interface IWADPicker : NSObject
|
||||
{
|
||||
NSApplication *app;
|
||||
NSWindow *window;
|
||||
NSButton *okButton;
|
||||
NSButton *cancelButton;
|
||||
NSButton *browseButton;
|
||||
NSTextField *parametersTextField;
|
||||
bool cancelled;
|
||||
}
|
||||
|
||||
- (void)buttonPressed:(id) sender;
|
||||
- (void)browseButtonPressed:(id) sender;
|
||||
- (void)doubleClicked:(id) sender;
|
||||
- (void)makeLabel:(NSTextField *)label withString:(const char*) str;
|
||||
- (int)pickIWad:(WadStuff *)wads num:(int) numwads showWindow:(bool) showwin defaultWad:(int) defaultiwad;
|
||||
- (NSString*)commandLineParameters;
|
||||
- (void)menuActionSent:(NSNotification*)notification;
|
||||
@end
|
||||
|
||||
@implementation IWADPicker
|
||||
|
||||
- (void)buttonPressed:(id) sender
|
||||
{
|
||||
if(sender == cancelButton)
|
||||
cancelled = true;
|
||||
|
||||
[window orderOut:self];
|
||||
[app stopModal];
|
||||
}
|
||||
|
||||
- (void)browseButtonPressed:(id) sender
|
||||
{
|
||||
NSOpenPanel* openPanel = [NSOpenPanel openPanel];
|
||||
[openPanel setAllowsMultipleSelection:YES];
|
||||
[openPanel setCanChooseFiles:YES];
|
||||
[openPanel setCanChooseDirectories:YES];
|
||||
[openPanel setResolvesAliases:YES];
|
||||
[openPanel setAllowedFileTypes:GetKnownExtensions()];
|
||||
|
||||
if (NSOKButton == [openPanel runModal])
|
||||
{
|
||||
NSArray* files = [openPanel URLs];
|
||||
NSMutableString* parameters = [NSMutableString string];
|
||||
|
||||
for (NSUInteger i = 0, ei = [files count]; i < ei; ++i)
|
||||
{
|
||||
NSString* filePath = [[files objectAtIndex:i] path];
|
||||
BOOL isDirectory = false;
|
||||
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDirectory] && isDirectory)
|
||||
{
|
||||
[parameters appendFormat:@"-file \"%@\" ", filePath];
|
||||
}
|
||||
else
|
||||
{
|
||||
[parameters appendKnownFileType:filePath];
|
||||
}
|
||||
}
|
||||
|
||||
if ([parameters length] > 0)
|
||||
{
|
||||
NSString* newParameters = [parametersTextField stringValue];
|
||||
|
||||
if ([newParameters length] > 0
|
||||
&& NO == [newParameters hasSuffix:@" "])
|
||||
{
|
||||
newParameters = [newParameters stringByAppendingString:@" "];
|
||||
}
|
||||
|
||||
newParameters = [newParameters stringByAppendingString:parameters];
|
||||
|
||||
[parametersTextField setStringValue: newParameters];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)doubleClicked:(id) sender
|
||||
{
|
||||
if ([sender clickedRow] >= 0)
|
||||
{
|
||||
[window orderOut:self];
|
||||
[app stopModal];
|
||||
}
|
||||
}
|
||||
|
||||
// Apparently labels in Cocoa are uneditable text fields, so lets make this a
|
||||
// little more automated.
|
||||
- (void)makeLabel:(NSTextField *)label withString:(const char*) str
|
||||
{
|
||||
[label setStringValue:[NSString stringWithUTF8String:str]];
|
||||
[label setBezeled:NO];
|
||||
[label setDrawsBackground:NO];
|
||||
[label setEditable:NO];
|
||||
[label setSelectable:NO];
|
||||
}
|
||||
|
||||
- (int)pickIWad:(WadStuff *)wads num:(int) numwads showWindow:(bool) showwin defaultWad:(int) defaultiwad
|
||||
{
|
||||
cancelled = false;
|
||||
|
||||
app = [NSApplication sharedApplication];
|
||||
id windowTitle = [NSString stringWithFormat:@"%s %s", GAMENAME, GetVersionString()];
|
||||
|
||||
NSRect frame = NSMakeRect(0, 0, 440, 450);
|
||||
window = [[NSWindow alloc] initWithContentRect:frame styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO];
|
||||
[window setTitle:windowTitle];
|
||||
|
||||
NSTextField *description = [[NSTextField alloc] initWithFrame:NSMakeRect(18, 384, 402, 50)];
|
||||
[self makeLabel:description withString:GAMENAME " found more than one IWAD\nSelect from the list below to determine which one to use:"];
|
||||
[[window contentView] addSubview:description];
|
||||
[description release];
|
||||
|
||||
NSScrollView *iwadScroller = [[NSScrollView alloc] initWithFrame:NSMakeRect(20, 135, 402, 256)];
|
||||
NSTableView *iwadTable = [[NSTableView alloc] initWithFrame:[iwadScroller bounds]];
|
||||
IWADTableData *tableData = [[IWADTableData alloc] init:wads num:numwads];
|
||||
for(int i = 0;i < NUM_COLUMNS;i++)
|
||||
{
|
||||
NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:[NSString stringWithUTF8String:tableHeaders[i]]];
|
||||
[[column headerCell] setStringValue:[column identifier]];
|
||||
if(i == 0)
|
||||
[column setMaxWidth:110];
|
||||
[column setEditable:NO];
|
||||
[column setResizingMask:NSTableColumnAutoresizingMask];
|
||||
[iwadTable addTableColumn:column];
|
||||
[column release];
|
||||
}
|
||||
[iwadScroller setDocumentView:iwadTable];
|
||||
[iwadScroller setHasVerticalScroller:YES];
|
||||
[iwadTable setDataSource:tableData];
|
||||
[iwadTable sizeToFit];
|
||||
[iwadTable setDoubleAction:@selector(doubleClicked:)];
|
||||
[iwadTable setTarget:self];
|
||||
NSIndexSet *selection = [[NSIndexSet alloc] initWithIndex:defaultiwad];
|
||||
[iwadTable selectRowIndexes:selection byExtendingSelection:NO];
|
||||
[selection release];
|
||||
[iwadTable scrollRowToVisible:defaultiwad];
|
||||
[[window contentView] addSubview:iwadScroller];
|
||||
[iwadTable release];
|
||||
[iwadScroller release];
|
||||
|
||||
NSTextField *additionalParametersLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(18, 108, 144, 17)];
|
||||
[self makeLabel:additionalParametersLabel withString:"Additional Parameters:"];
|
||||
[[window contentView] addSubview:additionalParametersLabel];
|
||||
parametersTextField = [[NSTextField alloc] initWithFrame:NSMakeRect(20, 48, 402, 54)];
|
||||
[parametersTextField setStringValue:[NSString stringWithUTF8String:osx_additional_parameters]];
|
||||
[[window contentView] addSubview:parametersTextField];
|
||||
|
||||
// Doesn't look like the SDL version implements this so lets not show it.
|
||||
/*NSButton *dontAsk = [[NSButton alloc] initWithFrame:NSMakeRect(18, 18, 178, 18)];
|
||||
[dontAsk setTitle:[NSString stringWithCString:"Don't ask me this again"]];
|
||||
[dontAsk setButtonType:NSSwitchButton];
|
||||
[dontAsk setState:(showwin ? NSOffState : NSOnState)];
|
||||
[[window contentView] addSubview:dontAsk];*/
|
||||
|
||||
okButton = [[NSButton alloc] initWithFrame:NSMakeRect(236, 8, 96, 32)];
|
||||
[okButton setTitle:@"OK"];
|
||||
[okButton setBezelStyle:NSRoundedBezelStyle];
|
||||
[okButton setAction:@selector(buttonPressed:)];
|
||||
[okButton setTarget:self];
|
||||
[okButton setKeyEquivalent:@"\r"];
|
||||
[[window contentView] addSubview:okButton];
|
||||
|
||||
cancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(332, 8, 96, 32)];
|
||||
[cancelButton setTitle:@"Cancel"];
|
||||
[cancelButton setBezelStyle:NSRoundedBezelStyle];
|
||||
[cancelButton setAction:@selector(buttonPressed:)];
|
||||
[cancelButton setTarget:self];
|
||||
[cancelButton setKeyEquivalent:@"\033"];
|
||||
[[window contentView] addSubview:cancelButton];
|
||||
|
||||
browseButton = [[NSButton alloc] initWithFrame:NSMakeRect(14, 8, 96, 32)];
|
||||
[browseButton setTitle:@"Browse..."];
|
||||
[browseButton setBezelStyle:NSRoundedBezelStyle];
|
||||
[browseButton setAction:@selector(browseButtonPressed:)];
|
||||
[browseButton setTarget:self];
|
||||
[[window contentView] addSubview:browseButton];
|
||||
|
||||
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
|
||||
[center addObserver:self selector:@selector(menuActionSent:) name:NSMenuDidSendActionNotification object:nil];
|
||||
|
||||
[window center];
|
||||
[app runModalForWindow:window];
|
||||
|
||||
[center removeObserver:self name:NSMenuDidSendActionNotification object:nil];
|
||||
|
||||
[window release];
|
||||
[okButton release];
|
||||
[cancelButton release];
|
||||
[browseButton release];
|
||||
|
||||
return cancelled ? -1 : [iwadTable selectedRow];
|
||||
}
|
||||
|
||||
- (NSString*)commandLineParameters
|
||||
{
|
||||
return [parametersTextField stringValue];
|
||||
}
|
||||
|
||||
- (void)menuActionSent:(NSNotification*)notification
|
||||
{
|
||||
NSDictionary* userInfo = [notification userInfo];
|
||||
NSMenuItem* menuItem = [userInfo valueForKey:@"MenuItem"];
|
||||
|
||||
if ( @selector(terminate:) == [menuItem action] )
|
||||
{
|
||||
throw CExitEvent(0);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
EXTERN_CVAR(String, defaultiwad)
|
||||
|
||||
static NSString* GetArchitectureString()
|
||||
{
|
||||
#ifdef __i386__
|
||||
return @"i386";
|
||||
#elif defined __x86_64__
|
||||
return @"x86_64";
|
||||
#endif
|
||||
}
|
||||
|
||||
static void RestartWithParameters(const WadStuff& wad, NSString* parameters)
|
||||
{
|
||||
assert(nil != parameters);
|
||||
|
||||
defaultiwad = wad.Name;
|
||||
|
||||
GameConfig->DoGameSetup("Doom");
|
||||
M_SaveDefaults(NULL);
|
||||
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
@try
|
||||
{
|
||||
NSString* executablePath = [NSString stringWithUTF8String:Args->GetArg(0)];
|
||||
|
||||
NSMutableArray* const arguments = [[NSMutableArray alloc] init];
|
||||
|
||||
// The following value shoud be equal to NSAppKitVersionNumber10_5
|
||||
// It's hard-coded in order to build with earlier SDKs
|
||||
const bool canSelectArchitecture = NSAppKitVersionNumber >= 949;
|
||||
|
||||
if (canSelectArchitecture)
|
||||
{
|
||||
[arguments addObject:@"-arch"];
|
||||
[arguments addObject:GetArchitectureString()];
|
||||
[arguments addObject:executablePath];
|
||||
|
||||
executablePath = @"/usr/bin/arch";
|
||||
}
|
||||
|
||||
[arguments addObject:@"-iwad"];
|
||||
[arguments addObject:[NSString stringWithUTF8String:wad.Path]];
|
||||
|
||||
for (int i = 1, count = Args->NumArgs(); i < count; ++i)
|
||||
{
|
||||
NSString* currentParameter = [NSString stringWithUTF8String:Args->GetArg(i)];
|
||||
[arguments addObject:currentParameter];
|
||||
}
|
||||
|
||||
wordexp_t expansion = {};
|
||||
|
||||
if (0 == wordexp([parameters UTF8String], &expansion, 0))
|
||||
{
|
||||
for (size_t i = 0; i < expansion.we_wordc; ++i)
|
||||
{
|
||||
NSString* argumentString = [NSString stringWithCString:expansion.we_wordv[i]
|
||||
encoding:NSUTF8StringEncoding];
|
||||
[arguments addObject:argumentString];
|
||||
}
|
||||
|
||||
wordfree(&expansion);
|
||||
}
|
||||
|
||||
[NSTask launchedTaskWithLaunchPath:executablePath
|
||||
arguments:arguments];
|
||||
|
||||
_exit(0); // to avoid atexit()'s functions
|
||||
}
|
||||
@catch (NSException* e)
|
||||
{
|
||||
NSLog(@"Cannot restart: %@", [e reason]);
|
||||
}
|
||||
|
||||
[pool release];
|
||||
}
|
||||
|
||||
// Simple wrapper so we can call this from outside.
|
||||
int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad)
|
||||
{
|
||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
IWADPicker *picker = [IWADPicker alloc];
|
||||
int ret = [picker pickIWad:wads num:numwads showWindow:showwin defaultWad:defaultiwad];
|
||||
|
||||
NSString* parametersToAppend = [picker commandLineParameters];
|
||||
osx_additional_parameters = [parametersToAppend UTF8String];
|
||||
|
||||
if (ret >= 0)
|
||||
{
|
||||
if (0 != [parametersToAppend length])
|
||||
{
|
||||
RestartWithParameters(wads[ret], parametersToAppend);
|
||||
}
|
||||
}
|
||||
|
||||
[pool release];
|
||||
|
||||
return ret;
|
||||
}
|
52
source/platform/posix/osx/zdoom-info.plist
Normal file
52
source/platform/posix/osx/zdoom-info.plist
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>zdoom.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.drdteam.gzdoom</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>GZDoom</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>Development Version</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.action-games</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.9</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Doom Resource File</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>wad</string>
|
||||
<string>pk3</string>
|
||||
<string>zip</string>
|
||||
<string>pk7</string>
|
||||
<string>7z</string>
|
||||
<string>iwad</string>
|
||||
<string>ipk3</string>
|
||||
<string>ipk7</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
BIN
source/platform/posix/osx/zdoom.icns
Normal file
BIN
source/platform/posix/osx/zdoom.icns
Normal file
Binary file not shown.
6
source/platform/posix/readme.md
Normal file
6
source/platform/posix/readme.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
This directory contains files required to support POSIX-compatible OSes, like GNU/Linux, OS X or BSD.
|
||||
|
||||
Common files are placed in this directory directly.
|
||||
SDL backend files are in `sdl` subdirectory.
|
||||
Native OS X backend files are in `cocoa` subdirectory.
|
||||
Shared files for both OS X backends are in `osx` subdirectory.
|
425
source/platform/posix/sdl/crashcatcher.c
Normal file
425
source/platform/posix/sdl/crashcatcher.c
Normal file
|
@ -0,0 +1,425 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/param.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/prctl.h>
|
||||
#ifndef PR_SET_PTRACER
|
||||
#define PR_SET_PTRACER 0x59616d61
|
||||
#endif
|
||||
#elif defined (__APPLE__) || defined (__FreeBSD__) || defined(__OpenBSD__)
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
|
||||
static const char crash_switch[] = "--cc-handle-crash";
|
||||
|
||||
static const char fatal_err[] = "\n\n*** Fatal Error ***\n";
|
||||
static const char pipe_err[] = "!!! Failed to create pipe\n";
|
||||
static const char fork_err[] = "!!! Failed to fork debug process\n";
|
||||
static const char exec_err[] = "!!! Failed to exec debug process\n";
|
||||
|
||||
static char argv0[PATH_MAX];
|
||||
|
||||
static char altstack[SIGSTKSZ];
|
||||
|
||||
|
||||
static struct {
|
||||
int signum;
|
||||
pid_t pid;
|
||||
int has_siginfo;
|
||||
siginfo_t siginfo;
|
||||
char buf[4096];
|
||||
} crash_info;
|
||||
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
int signum;
|
||||
} signals[] = {
|
||||
{ "Segmentation fault", SIGSEGV },
|
||||
{ "Illegal instruction", SIGILL },
|
||||
{ "FPU exception", SIGFPE },
|
||||
{ "System BUS error", SIGBUS },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
static const struct {
|
||||
int code;
|
||||
const char *name;
|
||||
} sigill_codes[] = {
|
||||
#ifndef __FreeBSD__
|
||||
{ ILL_ILLOPC, "Illegal opcode" },
|
||||
{ ILL_ILLOPN, "Illegal operand" },
|
||||
{ ILL_ILLADR, "Illegal addressing mode" },
|
||||
{ ILL_ILLTRP, "Illegal trap" },
|
||||
{ ILL_PRVOPC, "Privileged opcode" },
|
||||
{ ILL_PRVREG, "Privileged register" },
|
||||
{ ILL_COPROC, "Coprocessor error" },
|
||||
{ ILL_BADSTK, "Internal stack error" },
|
||||
#endif
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static const struct {
|
||||
int code;
|
||||
const char *name;
|
||||
} sigfpe_codes[] = {
|
||||
{ FPE_INTDIV, "Integer divide by zero" },
|
||||
{ FPE_INTOVF, "Integer overflow" },
|
||||
{ FPE_FLTDIV, "Floating point divide by zero" },
|
||||
{ FPE_FLTOVF, "Floating point overflow" },
|
||||
{ FPE_FLTUND, "Floating point underflow" },
|
||||
{ FPE_FLTRES, "Floating point inexact result" },
|
||||
{ FPE_FLTINV, "Floating point invalid operation" },
|
||||
{ FPE_FLTSUB, "Subscript out of range" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static const struct {
|
||||
int code;
|
||||
const char *name;
|
||||
} sigsegv_codes[] = {
|
||||
#ifndef __FreeBSD__
|
||||
{ SEGV_MAPERR, "Address not mapped to object" },
|
||||
{ SEGV_ACCERR, "Invalid permissions for mapped object" },
|
||||
#endif
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static const struct {
|
||||
int code;
|
||||
const char *name;
|
||||
} sigbus_codes[] = {
|
||||
#ifndef __FreeBSD__
|
||||
{ BUS_ADRALN, "Invalid address alignment" },
|
||||
{ BUS_ADRERR, "Non-existent physical address" },
|
||||
{ BUS_OBJERR, "Object specific hardware error" },
|
||||
#endif
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static int (*cc_user_info)(char*, char*);
|
||||
|
||||
|
||||
static void gdb_info(pid_t pid)
|
||||
{
|
||||
char respfile[64];
|
||||
char cmd_buf[128];
|
||||
FILE *f;
|
||||
int fd;
|
||||
|
||||
/* Create a temp file to put gdb commands into */
|
||||
strcpy(respfile, "gdb-respfile-XXXXXX");
|
||||
if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != NULL)
|
||||
{
|
||||
fprintf(f, "attach %d\n"
|
||||
"shell echo \"\"\n"
|
||||
"shell echo \"* Loaded Libraries\"\n"
|
||||
"info sharedlibrary\n"
|
||||
"shell echo \"\"\n"
|
||||
"shell echo \"* Threads\"\n"
|
||||
"info threads\n"
|
||||
"shell echo \"\"\n"
|
||||
"shell echo \"* FPU Status\"\n"
|
||||
"info float\n"
|
||||
"shell echo \"\"\n"
|
||||
"shell echo \"* Registers\"\n"
|
||||
"info registers\n"
|
||||
"shell echo \"\"\n"
|
||||
"shell echo \"* Backtrace\"\n"
|
||||
"thread apply all backtrace full\n"
|
||||
"detach\n"
|
||||
"quit\n", pid);
|
||||
fclose(f);
|
||||
|
||||
/* Run gdb and print process info. */
|
||||
snprintf(cmd_buf, sizeof(cmd_buf), "gdb --quiet --batch --command=%s", respfile);
|
||||
printf("Executing: %s\n", cmd_buf);
|
||||
fflush(stdout);
|
||||
|
||||
system(cmd_buf);
|
||||
/* Clean up */
|
||||
remove(respfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Error creating temp file */
|
||||
if(fd >= 0)
|
||||
{
|
||||
close(fd);
|
||||
remove(respfile);
|
||||
}
|
||||
printf("!!! Could not create gdb command file\n");
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static void sys_info(void)
|
||||
{
|
||||
#ifdef __unix__
|
||||
system("echo \"System: `uname -a`\"");
|
||||
putchar('\n');
|
||||
fflush(stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static size_t safe_write(int fd, const void *buf, size_t len)
|
||||
{
|
||||
size_t ret = 0;
|
||||
while(ret < len)
|
||||
{
|
||||
ssize_t rem;
|
||||
if((rem=write(fd, (const char*)buf+ret, len-ret)) == -1)
|
||||
{
|
||||
if(errno == EINTR)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
ret += rem;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void crash_catcher(int signum, siginfo_t *siginfo, void *context)
|
||||
{
|
||||
//ucontext_t *ucontext = (ucontext_t*)context;
|
||||
pid_t dbg_pid;
|
||||
int fd[2];
|
||||
|
||||
/* Make sure the effective uid is the real uid */
|
||||
if(getuid() != geteuid())
|
||||
{
|
||||
raise(signum);
|
||||
return;
|
||||
}
|
||||
|
||||
safe_write(STDERR_FILENO, fatal_err, sizeof(fatal_err)-1);
|
||||
if(pipe(fd) == -1)
|
||||
{
|
||||
safe_write(STDERR_FILENO, pipe_err, sizeof(pipe_err)-1);
|
||||
raise(signum);
|
||||
return;
|
||||
}
|
||||
|
||||
crash_info.signum = signum;
|
||||
crash_info.pid = getpid();
|
||||
crash_info.has_siginfo = !!siginfo;
|
||||
if(siginfo)
|
||||
crash_info.siginfo = *siginfo;
|
||||
if(cc_user_info)
|
||||
cc_user_info(crash_info.buf, crash_info.buf+sizeof(crash_info.buf));
|
||||
|
||||
/* Fork off to start a crash handler */
|
||||
switch((dbg_pid=fork()))
|
||||
{
|
||||
/* Error */
|
||||
case -1:
|
||||
safe_write(STDERR_FILENO, fork_err, sizeof(fork_err)-1);
|
||||
raise(signum);
|
||||
return;
|
||||
|
||||
case 0:
|
||||
dup2(fd[0], STDIN_FILENO);
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
|
||||
execl(argv0, argv0, crash_switch, NULL);
|
||||
|
||||
safe_write(STDERR_FILENO, exec_err, sizeof(exec_err)-1);
|
||||
_exit(1);
|
||||
|
||||
default:
|
||||
#ifdef __linux__
|
||||
prctl(PR_SET_PTRACER, dbg_pid, 0, 0, 0);
|
||||
#endif
|
||||
safe_write(fd[1], &crash_info, sizeof(crash_info));
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
|
||||
/* Wait; we'll be killed when gdb is done */
|
||||
do {
|
||||
int status;
|
||||
if(waitpid(dbg_pid, &status, 0) == dbg_pid &&
|
||||
(WIFEXITED(status) || WIFSIGNALED(status)))
|
||||
{
|
||||
/* The debug process died before it could kill us */
|
||||
raise(signum);
|
||||
break;
|
||||
}
|
||||
} while(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void crash_handler(const char *logfile)
|
||||
{
|
||||
const char *sigdesc = "";
|
||||
int i;
|
||||
|
||||
if(fread(&crash_info, sizeof(crash_info), 1, stdin) != 1)
|
||||
{
|
||||
fprintf(stderr, "!!! Failed to retrieve info from crashed process\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Get the signal description */
|
||||
for(i = 0;signals[i].name;++i)
|
||||
{
|
||||
if(signals[i].signum == crash_info.signum)
|
||||
{
|
||||
sigdesc = signals[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(crash_info.has_siginfo)
|
||||
{
|
||||
switch(crash_info.signum)
|
||||
{
|
||||
case SIGSEGV:
|
||||
for(i = 0;sigsegv_codes[i].name;++i)
|
||||
{
|
||||
if(sigsegv_codes[i].code == crash_info.siginfo.si_code)
|
||||
{
|
||||
sigdesc = sigsegv_codes[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SIGFPE:
|
||||
for(i = 0;sigfpe_codes[i].name;++i)
|
||||
{
|
||||
if(sigfpe_codes[i].code == crash_info.siginfo.si_code)
|
||||
{
|
||||
sigdesc = sigfpe_codes[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SIGILL:
|
||||
for(i = 0;sigill_codes[i].name;++i)
|
||||
{
|
||||
if(sigill_codes[i].code == crash_info.siginfo.si_code)
|
||||
{
|
||||
sigdesc = sigill_codes[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SIGBUS:
|
||||
for(i = 0;sigbus_codes[i].name;++i)
|
||||
{
|
||||
if(sigbus_codes[i].code == crash_info.siginfo.si_code)
|
||||
{
|
||||
sigdesc = sigbus_codes[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "%s (signal %i)\n", sigdesc, crash_info.signum);
|
||||
if(crash_info.has_siginfo)
|
||||
fprintf(stderr, "Address: %p\n", crash_info.siginfo.si_addr);
|
||||
fputc('\n', stderr);
|
||||
|
||||
if(logfile)
|
||||
{
|
||||
/* Create crash log file and redirect shell output to it */
|
||||
if(freopen(logfile, "wa", stdout) != stdout)
|
||||
{
|
||||
fprintf(stderr, "!!! Could not create %s following signal\n", logfile);
|
||||
exit(1);
|
||||
}
|
||||
fprintf(stderr, "Generating %s and killing process %d, please wait... ", logfile, crash_info.pid);
|
||||
|
||||
printf("*** Fatal Error ***\n"
|
||||
"%s (signal %i)\n", sigdesc, crash_info.signum);
|
||||
if(crash_info.has_siginfo)
|
||||
printf("Address: %p\n", crash_info.siginfo.si_addr);
|
||||
fputc('\n', stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
sys_info();
|
||||
|
||||
crash_info.buf[sizeof(crash_info.buf)-1] = '\0';
|
||||
printf("%s\n", crash_info.buf);
|
||||
fflush(stdout);
|
||||
|
||||
if(crash_info.pid > 0)
|
||||
{
|
||||
gdb_info(crash_info.pid);
|
||||
kill(crash_info.pid, SIGKILL);
|
||||
}
|
||||
|
||||
if(logfile)
|
||||
{
|
||||
const char *str;
|
||||
char buf[512];
|
||||
|
||||
if((str=getenv("KDE_FULL_SESSION")) && strcmp(str, "true") == 0)
|
||||
snprintf(buf, sizeof(buf), "kdialog --title \"Very Fatal Error\" --textbox \"%s\" 800 600", logfile);
|
||||
else if((str=getenv("GNOME_DESKTOP_SESSION_ID")) && str[0] != '\0')
|
||||
snprintf(buf, sizeof(buf), "gxmessage -buttons \"Okay:0\" -geometry 800x600 -title \"Very Fatal Error\" -center -file \"%s\"", logfile);
|
||||
else
|
||||
snprintf(buf, sizeof(buf), "xmessage -buttons \"Okay:0\" -center -file \"%s\"", logfile);
|
||||
|
||||
system(buf);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int cc_install_handlers(int argc, char **argv, int num_signals, int *signals, const char *logfile, int (*user_info)(char*, char*))
|
||||
{
|
||||
struct sigaction sa;
|
||||
stack_t altss;
|
||||
int retval;
|
||||
|
||||
if(argc == 2 && strcmp(argv[1], crash_switch) == 0)
|
||||
crash_handler(logfile);
|
||||
|
||||
cc_user_info = user_info;
|
||||
|
||||
if(argv[0][0] == '/')
|
||||
snprintf(argv0, sizeof(argv0), "%s", argv[0]);
|
||||
else
|
||||
{
|
||||
getcwd(argv0, sizeof(argv0));
|
||||
retval = strlen(argv0);
|
||||
snprintf(argv0+retval, sizeof(argv0)-retval, "/%s", argv[0]);
|
||||
}
|
||||
|
||||
/* Set an alternate signal stack so SIGSEGVs caused by stack overflows
|
||||
* still run */
|
||||
altss.ss_sp = altstack;
|
||||
altss.ss_flags = 0;
|
||||
altss.ss_size = sizeof(altstack);
|
||||
sigaltstack(&altss, NULL);
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_sigaction = crash_catcher;
|
||||
sa.sa_flags = SA_RESETHAND | SA_NODEFER | SA_SIGINFO | SA_ONSTACK;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
retval = 0;
|
||||
while(num_signals--)
|
||||
{
|
||||
if((*signals != SIGSEGV && *signals != SIGILL && *signals != SIGFPE &&
|
||||
*signals != SIGBUS) || sigaction(*signals, &sa, NULL) == -1)
|
||||
{
|
||||
*signals = 0;
|
||||
retval = -1;
|
||||
}
|
||||
++signals;
|
||||
}
|
||||
return retval;
|
||||
}
|
49
source/platform/posix/sdl/gl_sysfb.h
Normal file
49
source/platform/posix/sdl/gl_sysfb.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
#ifndef __POSIX_SDL_GL_SYSFB_H__
|
||||
#define __POSIX_SDL_GL_SYSFB_H__
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "v_video.h"
|
||||
|
||||
class SystemBaseFrameBuffer : public DFrameBuffer
|
||||
{
|
||||
typedef DFrameBuffer Super;
|
||||
|
||||
public:
|
||||
// this must have the same parameters as the Windows version, even if they are not used!
|
||||
SystemBaseFrameBuffer (void *hMonitor, bool fullscreen);
|
||||
|
||||
bool IsFullscreen() override;
|
||||
|
||||
int GetClientWidth() override;
|
||||
int GetClientHeight() override;
|
||||
|
||||
void ToggleFullscreen(bool yes) override;
|
||||
void SetWindowSize(int client_w, int client_h) override;
|
||||
|
||||
protected:
|
||||
SystemBaseFrameBuffer () {}
|
||||
};
|
||||
|
||||
class SystemGLFrameBuffer : public SystemBaseFrameBuffer
|
||||
{
|
||||
typedef SystemBaseFrameBuffer Super;
|
||||
|
||||
public:
|
||||
SystemGLFrameBuffer(void *hMonitor, bool fullscreen);
|
||||
~SystemGLFrameBuffer();
|
||||
|
||||
int GetClientWidth() override;
|
||||
int GetClientHeight() override;
|
||||
|
||||
virtual void SetVSync(bool vsync) override;
|
||||
void SwapBuffers();
|
||||
|
||||
protected:
|
||||
SDL_GLContext GLContext;
|
||||
|
||||
SystemGLFrameBuffer() {}
|
||||
};
|
||||
|
||||
#endif // __POSIX_SDL_GL_SYSFB_H__
|
||||
|
85
source/platform/posix/sdl/hardware.cpp
Normal file
85
source/platform/posix/sdl/hardware.cpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
** hardware.cpp
|
||||
** Somewhat OS-independant interface to the screen, mouse, keyboard, and stick
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2006 Randy Heit
|
||||
** 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 <SDL.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "i_system.h"
|
||||
#include "hardware.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "v_text.h"
|
||||
#include "doomstat.h"
|
||||
#include "m_argv.h"
|
||||
#include "doomerrors.h"
|
||||
#include "swrenderer/r_swrenderer.h"
|
||||
|
||||
IVideo *Video;
|
||||
|
||||
void I_RestartRenderer();
|
||||
|
||||
|
||||
void I_ShutdownGraphics ()
|
||||
{
|
||||
if (screen)
|
||||
{
|
||||
DFrameBuffer *s = screen;
|
||||
screen = NULL;
|
||||
delete s;
|
||||
}
|
||||
if (Video)
|
||||
delete Video, Video = NULL;
|
||||
|
||||
SDL_QuitSubSystem (SDL_INIT_VIDEO);
|
||||
}
|
||||
|
||||
void I_InitGraphics ()
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
SDL_SetHint(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, "0");
|
||||
#endif // __APPLE__
|
||||
|
||||
if (SDL_InitSubSystem (SDL_INIT_VIDEO) < 0)
|
||||
{
|
||||
I_FatalError ("Could not initialize SDL video:\n%s\n", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
Printf("Using video driver %s\n", SDL_GetCurrentVideoDriver());
|
||||
|
||||
extern IVideo *gl_CreateVideo();
|
||||
Video = gl_CreateVideo();
|
||||
|
||||
if (Video == NULL)
|
||||
I_FatalError ("Failed to initialize display");
|
||||
}
|
87
source/platform/posix/sdl/i_gui.cpp
Normal file
87
source/platform/posix/sdl/i_gui.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
** i_gui.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2008 Randy Heit
|
||||
** 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 <string.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "bitmap.h"
|
||||
#include "v_palette.h"
|
||||
#include "textures.h"
|
||||
|
||||
bool I_SetCursor(FTexture *cursorpic)
|
||||
{
|
||||
static SDL_Cursor *cursor;
|
||||
static SDL_Surface *cursorSurface;
|
||||
|
||||
if (cursorpic != NULL && cursorpic->isValid())
|
||||
{
|
||||
auto src = cursorpic->GetBgraBitmap(nullptr);
|
||||
// Must be no larger than 32x32.
|
||||
if (src.GetWidth() > 32 || src.GetHeight() > 32)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cursorSurface == NULL)
|
||||
cursorSurface = SDL_CreateRGBSurface (0, 32, 32, 32, MAKEARGB(0,255,0,0), MAKEARGB(0,0,255,0), MAKEARGB(0,0,0,255), MAKEARGB(255,0,0,0));
|
||||
|
||||
SDL_LockSurface(cursorSurface);
|
||||
uint8_t buffer[32*32*4];
|
||||
memset(buffer, 0, 32*32*4);
|
||||
FBitmap bmp(buffer, 32*4, 32, 32);
|
||||
bmp.Blit(0, 0, src); // expand to 32*32
|
||||
memcpy(cursorSurface->pixels, bmp.GetPixels(), 32*32*4);
|
||||
SDL_UnlockSurface(cursorSurface);
|
||||
|
||||
if (cursor)
|
||||
SDL_FreeCursor (cursor);
|
||||
cursor = SDL_CreateColorCursor (cursorSurface, 0, 0);
|
||||
SDL_SetCursor (cursor);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cursor)
|
||||
{
|
||||
SDL_SetCursor (NULL);
|
||||
SDL_FreeCursor (cursor);
|
||||
cursor = NULL;
|
||||
}
|
||||
if (cursorSurface != NULL)
|
||||
{
|
||||
SDL_FreeSurface(cursorSurface);
|
||||
cursorSurface = NULL;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
568
source/platform/posix/sdl/i_input.cpp
Normal file
568
source/platform/posix/sdl/i_input.cpp
Normal file
|
@ -0,0 +1,568 @@
|
|||
/*
|
||||
** i_input.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2005-2016 Randy Heit
|
||||
** 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 <SDL.h>
|
||||
#include "doomtype.h"
|
||||
#include "doomdef.h"
|
||||
#include "doomstat.h"
|
||||
#include "m_argv.h"
|
||||
#include "v_video.h"
|
||||
|
||||
#include "d_main.h"
|
||||
#include "d_event.h"
|
||||
#include "d_gui.h"
|
||||
#include "c_console.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "dikeys.h"
|
||||
#include "events.h"
|
||||
#include "g_game.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "utf8.h"
|
||||
#include "doomerrors.h"
|
||||
|
||||
|
||||
static void I_CheckGUICapture ();
|
||||
static void I_CheckNativeMouse ();
|
||||
|
||||
bool GUICapture;
|
||||
static bool NativeMouse = true;
|
||||
|
||||
extern int paused;
|
||||
|
||||
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)
|
||||
|
||||
|
||||
extern int WaitingForKey, chatmodeon;
|
||||
extern constate_e ConsoleState;
|
||||
|
||||
static const SDL_Keycode DIKToKeySym[256] =
|
||||
{
|
||||
0, SDLK_ESCAPE, SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_5, SDLK_6,
|
||||
SDLK_7, SDLK_8, SDLK_9, SDLK_0,SDLK_MINUS, SDLK_EQUALS, SDLK_BACKSPACE, SDLK_TAB,
|
||||
SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_y, SDLK_u, SDLK_i,
|
||||
SDLK_o, SDLK_p, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET, SDLK_RETURN, SDLK_LCTRL, SDLK_a, SDLK_s,
|
||||
SDLK_d, SDLK_f, SDLK_g, SDLK_h, SDLK_j, SDLK_k, SDLK_l, SDLK_SEMICOLON,
|
||||
SDLK_QUOTE, SDLK_BACKQUOTE, SDLK_LSHIFT, SDLK_BACKSLASH, SDLK_z, SDLK_x, SDLK_c, SDLK_v,
|
||||
SDLK_b, SDLK_n, SDLK_m, SDLK_COMMA, SDLK_PERIOD, SDLK_SLASH, SDLK_RSHIFT, SDLK_KP_MULTIPLY,
|
||||
SDLK_LALT, SDLK_SPACE, SDLK_CAPSLOCK, SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5,
|
||||
SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_NUMLOCKCLEAR, SDLK_SCROLLLOCK, SDLK_KP_7,
|
||||
SDLK_KP_8, SDLK_KP_9, SDLK_KP_MINUS, SDLK_KP_4, SDLK_KP_5, SDLK_KP_6, SDLK_KP_PLUS, SDLK_KP_1,
|
||||
SDLK_KP_2, SDLK_KP_3, SDLK_KP_0, SDLK_KP_PERIOD, 0, 0, 0, SDLK_F11,
|
||||
SDLK_F12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, SDLK_F13, SDLK_F14, SDLK_F15, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, SDLK_KP_EQUALS, 0, 0,
|
||||
0, SDLK_AT, SDLK_COLON, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, SDLK_KP_ENTER, SDLK_RCTRL, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, SDLK_KP_COMMA, 0, SDLK_KP_DIVIDE, 0, SDLK_SYSREQ,
|
||||
SDLK_RALT, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, SDLK_PAUSE, 0, SDLK_HOME,
|
||||
SDLK_UP, SDLK_PAGEUP, 0, SDLK_LEFT, 0, SDLK_RIGHT, 0, SDLK_END,
|
||||
SDLK_DOWN, SDLK_PAGEDOWN, SDLK_INSERT, SDLK_DELETE, 0, 0, 0, 0,
|
||||
0, 0, 0, SDLK_LGUI, SDLK_RGUI, SDLK_MENU, SDLK_POWER, SDLK_SLEEP,
|
||||
0, 0, 0, 0, 0, SDLK_AC_SEARCH, SDLK_AC_BOOKMARKS, SDLK_AC_REFRESH,
|
||||
SDLK_AC_STOP, SDLK_AC_FORWARD, SDLK_AC_BACK, SDLK_COMPUTER, SDLK_MAIL, SDLK_MEDIASELECT, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
static const SDL_Scancode DIKToKeyScan[256] =
|
||||
{
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_ESCAPE, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6,
|
||||
SDL_SCANCODE_7, SDL_SCANCODE_8, SDL_SCANCODE_9, SDL_SCANCODE_0 ,SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS, SDL_SCANCODE_BACKSPACE, SDL_SCANCODE_TAB,
|
||||
SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_R, SDL_SCANCODE_T, SDL_SCANCODE_Y, SDL_SCANCODE_U, SDL_SCANCODE_I,
|
||||
SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_RETURN, SDL_SCANCODE_LCTRL, SDL_SCANCODE_A, SDL_SCANCODE_S,
|
||||
SDL_SCANCODE_D, SDL_SCANCODE_F, SDL_SCANCODE_G, SDL_SCANCODE_H, SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_L, SDL_SCANCODE_SEMICOLON,
|
||||
SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_GRAVE, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_BACKSLASH, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_C, SDL_SCANCODE_V,
|
||||
SDL_SCANCODE_B, SDL_SCANCODE_N, SDL_SCANCODE_M, SDL_SCANCODE_COMMA, SDL_SCANCODE_PERIOD, SDL_SCANCODE_SLASH, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_KP_MULTIPLY,
|
||||
SDL_SCANCODE_LALT, SDL_SCANCODE_SPACE, SDL_SCANCODE_CAPSLOCK, SDL_SCANCODE_F1, SDL_SCANCODE_F2, SDL_SCANCODE_F3, SDL_SCANCODE_F4, SDL_SCANCODE_F5,
|
||||
SDL_SCANCODE_F6, SDL_SCANCODE_F7, SDL_SCANCODE_F8, SDL_SCANCODE_F9, SDL_SCANCODE_F10, SDL_SCANCODE_NUMLOCKCLEAR, SDL_SCANCODE_SCROLLLOCK, SDL_SCANCODE_KP_7,
|
||||
SDL_SCANCODE_KP_8, SDL_SCANCODE_KP_9, SDL_SCANCODE_KP_MINUS, SDL_SCANCODE_KP_4, SDL_SCANCODE_KP_5, SDL_SCANCODE_KP_6, SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_KP_1,
|
||||
SDL_SCANCODE_KP_2, SDL_SCANCODE_KP_3, SDL_SCANCODE_KP_0, SDL_SCANCODE_KP_PERIOD, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F11,
|
||||
SDL_SCANCODE_F12, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F13, SDL_SCANCODE_F14, SDL_SCANCODE_F15, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_EQUALS, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_ENTER, SDL_SCANCODE_RCTRL, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_COMMA, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_SYSREQ,
|
||||
SDL_SCANCODE_RALT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_PAUSE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_HOME,
|
||||
SDL_SCANCODE_UP, SDL_SCANCODE_PAGEUP, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_END,
|
||||
SDL_SCANCODE_DOWN, SDL_SCANCODE_PAGEDOWN, SDL_SCANCODE_INSERT, SDL_SCANCODE_DELETE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LGUI, SDL_SCANCODE_RGUI, SDL_SCANCODE_MENU, SDL_SCANCODE_POWER, SDL_SCANCODE_SLEEP,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_AC_SEARCH, SDL_SCANCODE_AC_BOOKMARKS, SDL_SCANCODE_AC_REFRESH,
|
||||
SDL_SCANCODE_AC_STOP, SDL_SCANCODE_AC_FORWARD, SDL_SCANCODE_AC_BACK, SDL_SCANCODE_COMPUTER, SDL_SCANCODE_MAIL, SDL_SCANCODE_MEDIASELECT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN
|
||||
};
|
||||
|
||||
static TMap<SDL_Keycode, uint8_t> InitKeySymMap ()
|
||||
{
|
||||
TMap<SDL_Keycode, uint8_t> KeySymToDIK;
|
||||
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
KeySymToDIK[DIKToKeySym[i]] = i;
|
||||
}
|
||||
KeySymToDIK[0] = 0;
|
||||
KeySymToDIK[SDLK_RSHIFT] = DIK_LSHIFT;
|
||||
KeySymToDIK[SDLK_RCTRL] = DIK_LCONTROL;
|
||||
KeySymToDIK[SDLK_RALT] = DIK_LMENU;
|
||||
// Depending on your Linux flavor, you may get SDLK_PRINT or SDLK_SYSREQ
|
||||
KeySymToDIK[SDLK_PRINTSCREEN] = DIK_SYSRQ;
|
||||
|
||||
return KeySymToDIK;
|
||||
}
|
||||
static const TMap<SDL_Keycode, uint8_t> KeySymToDIK(InitKeySymMap());
|
||||
|
||||
static TMap<SDL_Scancode, uint8_t> InitKeyScanMap ()
|
||||
{
|
||||
TMap<SDL_Scancode, uint8_t> KeyScanToDIK;
|
||||
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
KeyScanToDIK[DIKToKeyScan[i]] = i;
|
||||
}
|
||||
|
||||
return KeyScanToDIK;
|
||||
}
|
||||
static const TMap<SDL_Scancode, uint8_t> KeyScanToDIK(InitKeyScanMap());
|
||||
|
||||
static void I_CheckGUICapture ()
|
||||
{
|
||||
bool wantCapt;
|
||||
|
||||
if (menuactive == MENU_Off)
|
||||
{
|
||||
wantCapt = ConsoleState == c_down || ConsoleState == c_falling || chatmodeon;
|
||||
}
|
||||
else
|
||||
{
|
||||
wantCapt = (menuactive == MENU_On || menuactive == MENU_OnNoPause);
|
||||
}
|
||||
|
||||
// [ZZ] check active event handlers that want the UI processing
|
||||
if (!wantCapt && primaryLevel->localEventManager->CheckUiProcessors())
|
||||
wantCapt = true;
|
||||
|
||||
if (wantCapt != GUICapture)
|
||||
{
|
||||
GUICapture = wantCapt;
|
||||
ResetButtonStates();
|
||||
}
|
||||
}
|
||||
|
||||
void I_SetMouseCapture()
|
||||
{
|
||||
// Clear out any mouse movement.
|
||||
SDL_GetRelativeMouseState (NULL, NULL);
|
||||
SDL_SetRelativeMouseMode (SDL_TRUE);
|
||||
}
|
||||
|
||||
void I_ReleaseMouseCapture()
|
||||
{
|
||||
SDL_SetRelativeMouseMode (SDL_FALSE);
|
||||
}
|
||||
|
||||
static void PostMouseMove (int x, int y)
|
||||
{
|
||||
static int lastx = 0, lasty = 0;
|
||||
event_t ev = { 0,0,0,0,0,0,0 };
|
||||
|
||||
if (m_filter)
|
||||
{
|
||||
ev.x = (x + lastx) / 2;
|
||||
ev.y = (y + lasty) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
ev.x = x;
|
||||
ev.y = y;
|
||||
}
|
||||
lastx = x;
|
||||
lasty = y;
|
||||
if (ev.x | ev.y)
|
||||
{
|
||||
ev.type = EV_Mouse;
|
||||
D_PostEvent (&ev);
|
||||
}
|
||||
}
|
||||
|
||||
static void MouseRead ()
|
||||
{
|
||||
int x, y;
|
||||
|
||||
if (NativeMouse)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_GetRelativeMouseState (&x, &y);
|
||||
if (!m_noprescale)
|
||||
{
|
||||
x *= 3;
|
||||
y *= 2;
|
||||
}
|
||||
if (x | y)
|
||||
{
|
||||
PostMouseMove (x, -y);
|
||||
}
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
|
||||
{
|
||||
if (self < 0) self = 0;
|
||||
else if (self > 2) self = 2;
|
||||
}
|
||||
|
||||
static bool inGame()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void I_CheckNativeMouse ()
|
||||
{
|
||||
bool focus = SDL_GetKeyboardFocus() != NULL;
|
||||
bool fs = screen->IsFullscreen();
|
||||
|
||||
bool wantNative = !focus || (!use_mouse || GUICapture || paused || demoplayback || !inGame());
|
||||
|
||||
if (wantNative != NativeMouse)
|
||||
{
|
||||
NativeMouse = wantNative;
|
||||
SDL_ShowCursor (wantNative);
|
||||
if (wantNative)
|
||||
I_ReleaseMouseCapture ();
|
||||
else
|
||||
I_SetMouseCapture ();
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePump (const SDL_Event &sev)
|
||||
{
|
||||
static int lastx = 0, lasty = 0;
|
||||
int x, y;
|
||||
event_t event = { 0,0,0,0,0,0,0 };
|
||||
|
||||
switch (sev.type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
throw CExitEvent(0);
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
extern void ProcessSDLWindowEvent(const SDL_WindowEvent &);
|
||||
ProcessSDLWindowEvent(sev.window);
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
if (!GUICapture)
|
||||
{
|
||||
event.type = sev.type == SDL_MOUSEBUTTONDOWN ? EV_KeyDown : EV_KeyUp;
|
||||
|
||||
switch (sev.button.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT: event.data1 = KEY_MOUSE1; break;
|
||||
case SDL_BUTTON_MIDDLE: event.data1 = KEY_MOUSE3; break;
|
||||
case SDL_BUTTON_RIGHT: event.data1 = KEY_MOUSE2; break;
|
||||
case SDL_BUTTON_X1: event.data1 = KEY_MOUSE4; break;
|
||||
case SDL_BUTTON_X2: event.data1 = KEY_MOUSE5; break;
|
||||
case 6: event.data1 = KEY_MOUSE6; break;
|
||||
case 7: event.data1 = KEY_MOUSE7; break;
|
||||
case 8: event.data1 = KEY_MOUSE8; break;
|
||||
default: printf("SDL mouse button %s %d\n",
|
||||
sev.type == SDL_MOUSEBUTTONDOWN ? "down" : "up", sev.button.button); break;
|
||||
}
|
||||
|
||||
if (event.data1 != 0)
|
||||
{
|
||||
D_PostEvent(&event);
|
||||
}
|
||||
}
|
||||
else if ((sev.button.button >= SDL_BUTTON_LEFT && sev.button.button <= SDL_BUTTON_X2))
|
||||
{
|
||||
int x, y;
|
||||
SDL_GetMouseState(&x, &y);
|
||||
|
||||
event.type = EV_GUI_Event;
|
||||
event.data1 = x;
|
||||
event.data2 = y;
|
||||
|
||||
screen->ScaleCoordsFromWindow(event.data1, event.data2);
|
||||
|
||||
if (sev.type == SDL_MOUSEBUTTONDOWN)
|
||||
{
|
||||
switch(sev.button.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT: event.subtype = EV_GUI_LButtonDown; break;
|
||||
case SDL_BUTTON_MIDDLE: event.subtype = EV_GUI_MButtonDown; break;
|
||||
case SDL_BUTTON_RIGHT: event.subtype = EV_GUI_RButtonDown; break;
|
||||
case SDL_BUTTON_X1: event.subtype = EV_GUI_BackButtonDown; break;
|
||||
case SDL_BUTTON_X2: event.subtype = EV_GUI_FwdButtonDown; break;
|
||||
default: assert(false); event.subtype = EV_GUI_None; break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(sev.button.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT: event.subtype = EV_GUI_LButtonUp; break;
|
||||
case SDL_BUTTON_MIDDLE: event.subtype = EV_GUI_MButtonUp; break;
|
||||
case SDL_BUTTON_RIGHT: event.subtype = EV_GUI_RButtonUp; break;
|
||||
case SDL_BUTTON_X1: event.subtype = EV_GUI_BackButtonUp; break;
|
||||
case SDL_BUTTON_X2: event.subtype = EV_GUI_FwdButtonUp; break;
|
||||
default: assert(false); event.subtype = EV_GUI_None; break;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Keymod kmod = SDL_GetModState();
|
||||
event.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) |
|
||||
((kmod & KMOD_CTRL) ? GKM_CTRL : 0) |
|
||||
((kmod & KMOD_ALT) ? GKM_ALT : 0);
|
||||
|
||||
D_PostEvent(&event);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEMOTION:
|
||||
if (GUICapture)
|
||||
{
|
||||
event.data1 = sev.motion.x;
|
||||
event.data2 = sev.motion.y;
|
||||
|
||||
screen->ScaleCoordsFromWindow(event.data1, event.data2);
|
||||
|
||||
event.type = EV_GUI_Event;
|
||||
event.subtype = EV_GUI_MouseMove;
|
||||
|
||||
SDL_Keymod kmod = SDL_GetModState();
|
||||
event.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) |
|
||||
((kmod & KMOD_CTRL) ? GKM_CTRL : 0) |
|
||||
((kmod & KMOD_ALT) ? GKM_ALT : 0);
|
||||
|
||||
D_PostEvent(&event);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEWHEEL:
|
||||
if (GUICapture)
|
||||
{
|
||||
event.type = EV_GUI_Event;
|
||||
|
||||
if (sev.wheel.y == 0)
|
||||
event.subtype = sev.wheel.x > 0 ? EV_GUI_WheelRight : EV_GUI_WheelLeft;
|
||||
else
|
||||
event.subtype = sev.wheel.y > 0 ? EV_GUI_WheelUp : EV_GUI_WheelDown;
|
||||
|
||||
SDL_Keymod kmod = SDL_GetModState();
|
||||
event.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) |
|
||||
((kmod & KMOD_CTRL) ? GKM_CTRL : 0) |
|
||||
((kmod & KMOD_ALT) ? GKM_ALT : 0);
|
||||
|
||||
D_PostEvent (&event);
|
||||
}
|
||||
else
|
||||
{
|
||||
event.type = EV_KeyDown;
|
||||
|
||||
if (sev.wheel.y != 0)
|
||||
event.data1 = sev.wheel.y > 0 ? KEY_MWHEELUP : KEY_MWHEELDOWN;
|
||||
else
|
||||
event.data1 = sev.wheel.x > 0 ? KEY_MWHEELRIGHT : KEY_MWHEELLEFT;
|
||||
|
||||
D_PostEvent (&event);
|
||||
event.type = EV_KeyUp;
|
||||
D_PostEvent (&event);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
if (!GUICapture)
|
||||
{
|
||||
if (sev.key.repeat)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
event.type = sev.type == SDL_KEYDOWN ? EV_KeyDown : EV_KeyUp;
|
||||
|
||||
// Try to look up our key mapped key for conversion to DirectInput.
|
||||
// If that fails, then we'll do a lookup against the scan code,
|
||||
// which may not return the right key, but at least the key should
|
||||
// work in the game.
|
||||
if (const uint8_t *dik = KeySymToDIK.CheckKey (sev.key.keysym.sym))
|
||||
event.data1 = *dik;
|
||||
else if (const uint8_t *dik = KeyScanToDIK.CheckKey (sev.key.keysym.scancode))
|
||||
event.data1 = *dik;
|
||||
|
||||
if (event.data1)
|
||||
{
|
||||
if (sev.key.keysym.sym < 256)
|
||||
{
|
||||
event.data2 = sev.key.keysym.sym;
|
||||
}
|
||||
D_PostEvent (&event);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
event.type = EV_GUI_Event;
|
||||
event.subtype = sev.type == SDL_KEYDOWN ? EV_GUI_KeyDown : EV_GUI_KeyUp;
|
||||
SDL_Keymod kmod = SDL_GetModState();
|
||||
event.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) |
|
||||
((kmod & KMOD_CTRL) ? GKM_CTRL : 0) |
|
||||
((kmod & KMOD_ALT) ? GKM_ALT : 0);
|
||||
|
||||
if (event.subtype == EV_GUI_KeyDown && sev.key.repeat)
|
||||
{
|
||||
event.subtype = EV_GUI_KeyRepeat;
|
||||
}
|
||||
|
||||
switch (sev.key.keysym.sym)
|
||||
{
|
||||
case SDLK_KP_ENTER: event.data1 = GK_RETURN; break;
|
||||
case SDLK_PAGEUP: event.data1 = GK_PGUP; break;
|
||||
case SDLK_PAGEDOWN: event.data1 = GK_PGDN; break;
|
||||
case SDLK_END: event.data1 = GK_END; break;
|
||||
case SDLK_HOME: event.data1 = GK_HOME; break;
|
||||
case SDLK_LEFT: event.data1 = GK_LEFT; break;
|
||||
case SDLK_RIGHT: event.data1 = GK_RIGHT; break;
|
||||
case SDLK_UP: event.data1 = GK_UP; break;
|
||||
case SDLK_DOWN: event.data1 = GK_DOWN; break;
|
||||
case SDLK_DELETE: event.data1 = GK_DEL; break;
|
||||
case SDLK_ESCAPE: event.data1 = GK_ESCAPE; break;
|
||||
case SDLK_F1: event.data1 = GK_F1; break;
|
||||
case SDLK_F2: event.data1 = GK_F2; break;
|
||||
case SDLK_F3: event.data1 = GK_F3; break;
|
||||
case SDLK_F4: event.data1 = GK_F4; break;
|
||||
case SDLK_F5: event.data1 = GK_F5; break;
|
||||
case SDLK_F6: event.data1 = GK_F6; break;
|
||||
case SDLK_F7: event.data1 = GK_F7; break;
|
||||
case SDLK_F8: event.data1 = GK_F8; break;
|
||||
case SDLK_F9: event.data1 = GK_F9; break;
|
||||
case SDLK_F10: event.data1 = GK_F10; break;
|
||||
case SDLK_F11: event.data1 = GK_F11; break;
|
||||
case SDLK_F12: event.data1 = GK_F12; break;
|
||||
default:
|
||||
if (sev.key.keysym.sym < 256)
|
||||
{
|
||||
event.data1 = sev.key.keysym.sym;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (event.data1 < 128)
|
||||
{
|
||||
event.data1 = toupper(event.data1);
|
||||
D_PostEvent (&event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_TEXTINPUT:
|
||||
if (GUICapture)
|
||||
{
|
||||
int size;
|
||||
|
||||
int unichar = utf8_decode((const uint8_t*)sev.text.text, &size);
|
||||
if (size != 4)
|
||||
{
|
||||
event.type = EV_GUI_Event;
|
||||
event.subtype = EV_GUI_Char;
|
||||
event.data1 = (int16_t)unichar;
|
||||
event.data2 = !!(SDL_GetModState() & KMOD_ALT);
|
||||
D_PostEvent (&event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_JOYBUTTONDOWN:
|
||||
case SDL_JOYBUTTONUP:
|
||||
if (!GUICapture)
|
||||
{
|
||||
event.type = sev.type == SDL_JOYBUTTONDOWN ? EV_KeyDown : EV_KeyUp;
|
||||
event.data1 = KEY_FIRSTJOYBUTTON + sev.jbutton.button;
|
||||
if(event.data1 != 0)
|
||||
D_PostEvent(&event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void I_GetEvent ()
|
||||
{
|
||||
SDL_Event sev;
|
||||
|
||||
while (SDL_PollEvent (&sev))
|
||||
{
|
||||
MessagePump (sev);
|
||||
}
|
||||
if (use_mouse)
|
||||
{
|
||||
MouseRead ();
|
||||
}
|
||||
}
|
||||
|
||||
void I_StartTic ()
|
||||
{
|
||||
I_CheckGUICapture ();
|
||||
I_CheckNativeMouse ();
|
||||
I_GetEvent ();
|
||||
}
|
||||
|
||||
void I_ProcessJoysticks ();
|
||||
void I_StartFrame ()
|
||||
{
|
||||
I_ProcessJoysticks();
|
||||
}
|
|
@ -332,7 +332,7 @@ void I_GetAxes(float axes[NUM_JOYAXIS])
|
|||
|
||||
void I_ProcessJoysticks()
|
||||
{
|
||||
if (use_joystick && JoystickManager)
|
||||
if (use_joystick)
|
||||
JoystickManager->ProcessInput();
|
||||
}
|
||||
|
211
source/platform/posix/sdl/i_main.cpp
Normal file
211
source/platform/posix/sdl/i_main.cpp
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
** i_main.cpp
|
||||
** System-specific startup code. Eventually calls D_DoomMain.
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2007 Randy Heit
|
||||
** 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
// HEADER FILES ------------------------------------------------------------
|
||||
|
||||
#include <SDL.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <new>
|
||||
#include <sys/param.h>
|
||||
#include <locale.h>
|
||||
|
||||
#include "doomerrors.h"
|
||||
#include "m_argv.h"
|
||||
#include "d_main.h"
|
||||
#include "c_console.h"
|
||||
#include "version.h"
|
||||
#include "w_wad.h"
|
||||
#include "g_level.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "cmdlib.h"
|
||||
#include "r_utility.h"
|
||||
#include "doomstat.h"
|
||||
#include "vm.h"
|
||||
#include "doomerrors.h"
|
||||
#include "i_system.h"
|
||||
#include "g_game.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
// TYPES -------------------------------------------------------------------
|
||||
|
||||
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
||||
|
||||
extern "C" int cc_install_handlers(int, char**, int, int*, const char*, int(*)(char*, char*));
|
||||
|
||||
#ifdef __APPLE__
|
||||
void Mac_I_FatalError(const char* errortext);
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
void Linux_I_FatalError(const char* errortext);
|
||||
#endif
|
||||
|
||||
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
||||
|
||||
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
||||
|
||||
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
||||
|
||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||
|
||||
// The command line arguments.
|
||||
FArgs *Args;
|
||||
|
||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
static int DoomSpecificInfo (char *buffer, char *end)
|
||||
{
|
||||
const char *arg;
|
||||
int size = end-buffer-2;
|
||||
int i, p;
|
||||
|
||||
p = 0;
|
||||
p += snprintf (buffer+p, size-p, GAMENAME" version %s (%s)\n", GetVersionString(), GetGitHash());
|
||||
#ifdef __VERSION__
|
||||
p += snprintf (buffer+p, size-p, "Compiler version: %s\n", __VERSION__);
|
||||
#endif
|
||||
|
||||
// If Args is nullptr, then execution is at either
|
||||
// * early stage of initialization, additional info contains only default values
|
||||
// * late stage of shutdown, most likely main() was done, and accessing global variables is no longer safe
|
||||
if (Args)
|
||||
{
|
||||
p += snprintf(buffer + p, size - p, "\nCommand line:");
|
||||
for (i = 0; i < Args->NumArgs(); ++i)
|
||||
{
|
||||
p += snprintf(buffer + p, size - p, " %s", Args->GetArg(i));
|
||||
}
|
||||
p += snprintf(buffer + p, size - p, "\n");
|
||||
|
||||
for (i = 0; (arg = Wads.GetWadName(i)) != NULL; ++i)
|
||||
{
|
||||
p += snprintf(buffer + p, size - p, "\nWad %d: %s", i, arg);
|
||||
}
|
||||
|
||||
if (gamestate != GS_LEVEL && gamestate != GS_TITLELEVEL)
|
||||
{
|
||||
p += snprintf(buffer + p, size - p, "\n\nNot in a level.");
|
||||
}
|
||||
else
|
||||
{
|
||||
p += snprintf(buffer + p, size - p, "\n\nCurrent map: %s", primaryLevel->MapName.GetChars());
|
||||
|
||||
if (!viewactive)
|
||||
{
|
||||
p += snprintf(buffer + p, size - p, "\n\nView not active.");
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& vp = r_viewpoint;
|
||||
p += snprintf(buffer + p, size - p, "\n\nviewx = %f", vp.Pos.X);
|
||||
p += snprintf(buffer + p, size - p, "\nviewy = %f", vp.Pos.Y);
|
||||
p += snprintf(buffer + p, size - p, "\nviewz = %f", vp.Pos.Z);
|
||||
p += snprintf(buffer + p, size - p, "\nviewangle = %f", vp.Angles.Yaw.Degrees);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer[p++] = '\n';
|
||||
buffer[p++] = '\0';
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void I_DetectOS()
|
||||
{
|
||||
// The POSIX version never implemented this.
|
||||
}
|
||||
|
||||
void I_StartupJoysticks();
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
#if !defined (__APPLE__)
|
||||
{
|
||||
int s[4] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS };
|
||||
cc_install_handlers(argc, argv, 4, s, GAMENAMELOWERCASE "-crash.log", DoomSpecificInfo);
|
||||
}
|
||||
#endif // !__APPLE__
|
||||
|
||||
printf(GAMENAME" %s - %s - SDL version\nCompiled on %s\n",
|
||||
GetVersionString(), GetGitTime(), __DATE__);
|
||||
|
||||
seteuid (getuid ());
|
||||
// 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");
|
||||
|
||||
if (SDL_Init (0) < 0)
|
||||
{
|
||||
fprintf (stderr, "Could not initialize SDL:\n%s\n", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
Args = new FArgs(argc, argv);
|
||||
|
||||
// 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();
|
||||
|
||||
const int result = D_DoomMain();
|
||||
|
||||
SDL_Quit();
|
||||
|
||||
return result;
|
||||
}
|
418
source/platform/posix/sdl/i_system.cpp
Normal file
418
source/platform/posix/sdl/i_system.cpp
Normal file
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
** i_system.cpp
|
||||
** Main startup code
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1999-2016 Randy Heit
|
||||
** Copyright 2019-2020 Christoph Oelckers
|
||||
** 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 "i_system.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fnmatch.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "doomerrors.h"
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "doomstat.h"
|
||||
#include "version.h"
|
||||
#include "doomdef.h"
|
||||
#include "cmdlib.h"
|
||||
#include "m_argv.h"
|
||||
#include "m_misc.h"
|
||||
#include "i_sound.h"
|
||||
#include "x86.h"
|
||||
|
||||
#include "d_main.h"
|
||||
#include "d_net.h"
|
||||
#include "g_game.h"
|
||||
#include "c_dispatch.h"
|
||||
|
||||
#include "gameconfigfile.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
double SecondsPerCycle = 1e-8;
|
||||
double CyclesPerSecond = 1e8;
|
||||
}
|
||||
|
||||
#ifndef NO_GTK
|
||||
bool I_GtkAvailable ();
|
||||
int I_PickIWad_Gtk (WadStuff *wads, int numwads, bool showwin, int defaultiwad);
|
||||
void I_ShowFatalError_Gtk(const char* errortext);
|
||||
#elif defined(__APPLE__)
|
||||
int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad);
|
||||
#endif
|
||||
|
||||
double PerfToSec, PerfToMillisec;
|
||||
|
||||
ticcmd_t emptycmd;
|
||||
ticcmd_t *I_BaseTiccmd(void)
|
||||
{
|
||||
return &emptycmd;
|
||||
}
|
||||
|
||||
void I_BeginRead(void)
|
||||
{
|
||||
}
|
||||
|
||||
void I_EndRead(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// I_Init
|
||||
//
|
||||
void I_Init (void)
|
||||
{
|
||||
CheckCPUID (&CPU);
|
||||
DumpCPUInfo (&CPU);
|
||||
}
|
||||
|
||||
//
|
||||
// I_Error
|
||||
//
|
||||
extern FILE *Logfile;
|
||||
|
||||
#ifdef __APPLE__
|
||||
void Mac_I_FatalError(const char* errortext);
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
void Linux_I_FatalError(const char* errortext)
|
||||
{
|
||||
// Close window or exit fullscreen and release mouse capture
|
||||
SDL_Quit();
|
||||
|
||||
const char *str;
|
||||
if((str=getenv("KDE_FULL_SESSION")) && strcmp(str, "true") == 0)
|
||||
{
|
||||
FString cmd;
|
||||
cmd << "kdialog --title \"" GAMESIG " " << GetVersionString()
|
||||
<< "\" --msgbox \"" << errortext << "\"";
|
||||
popen(cmd, "r");
|
||||
}
|
||||
#ifndef NO_GTK
|
||||
else if (I_GtkAvailable())
|
||||
{
|
||||
I_ShowFatalError_Gtk(errortext);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
FString title;
|
||||
title << GAMESIG " " << GetVersionString();
|
||||
|
||||
if (SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, errortext, NULL) < 0)
|
||||
{
|
||||
printf("\n%s\n", errortext);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void I_ShowFatalError(const char *message)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
Mac_I_FatalError(message);
|
||||
#elif defined __linux__
|
||||
Linux_I_FatalError(message);
|
||||
#else
|
||||
// ???
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
void I_SetIWADInfo ()
|
||||
{
|
||||
}
|
||||
|
||||
void I_DebugPrint(const char *cp)
|
||||
{
|
||||
}
|
||||
|
||||
void I_PrintStr(const char *cp)
|
||||
{
|
||||
// Strip out any color escape sequences before writing to debug output
|
||||
TArray<char> copy(strlen(cp) + 1, true);
|
||||
const char * srcp = cp;
|
||||
char * dstp = copy.Data();
|
||||
|
||||
while (*srcp != 0)
|
||||
{
|
||||
if (*srcp != 0x1c && *srcp != 0x1d && *srcp != 0x1e && *srcp != 0x1f)
|
||||
{
|
||||
*dstp++ = *srcp++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (srcp[1] != 0) srcp += 2;
|
||||
else break;
|
||||
}
|
||||
}
|
||||
*dstp = 0;
|
||||
|
||||
fputs(copy.Data(), stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!showwin)
|
||||
{
|
||||
return defaultiwad;
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
const char *str;
|
||||
if((str=getenv("KDE_FULL_SESSION")) && strcmp(str, "true") == 0)
|
||||
{
|
||||
FString cmd("kdialog --title \"" GAMESIG " ");
|
||||
cmd << GetVersionString() << ": Select an IWAD to use\""
|
||||
" --menu \"" GAMENAME " found more than one IWAD\n"
|
||||
"Select from the list below to determine which one to use:\"";
|
||||
|
||||
for(i = 0; i < numwads; ++i)
|
||||
{
|
||||
const char *filepart = strrchr(wads[i].Path, '/');
|
||||
if(filepart == NULL)
|
||||
filepart = wads[i].Path;
|
||||
else
|
||||
filepart++;
|
||||
// Menu entries are specified in "tag" "item" pairs, where when a
|
||||
// particular item is selected (and the Okay button clicked), its
|
||||
// corresponding tag is printed to stdout for identification.
|
||||
cmd.AppendFormat(" \"%d\" \"%s (%s)\"", i, wads[i].Name.GetChars(), filepart);
|
||||
}
|
||||
|
||||
if(defaultiwad >= 0 && defaultiwad < numwads)
|
||||
{
|
||||
const char *filepart = strrchr(wads[defaultiwad].Path, '/');
|
||||
if(filepart == NULL)
|
||||
filepart = wads[defaultiwad].Path;
|
||||
else
|
||||
filepart++;
|
||||
cmd.AppendFormat(" --default \"%s (%s)\"", wads[defaultiwad].Name.GetChars(), filepart);
|
||||
}
|
||||
|
||||
FILE *f = popen(cmd, "r");
|
||||
if(f != NULL)
|
||||
{
|
||||
char gotstr[16];
|
||||
|
||||
if(fgets(gotstr, sizeof(gotstr), f) == NULL ||
|
||||
sscanf(gotstr, "%d", &i) != 1)
|
||||
i = -1;
|
||||
|
||||
// Exit status = 1 means the selection was canceled (either by
|
||||
// Cancel/Esc or the X button), not that there was an error running
|
||||
// the program. In that case, nothing was printed so fgets will
|
||||
// have failed. Other values can indicate an error running the app,
|
||||
// so fall back to whatever else can be used.
|
||||
int status = pclose(f);
|
||||
if(WIFEXITED(status) && (WEXITSTATUS(status) == 0 || WEXITSTATUS(status) == 1))
|
||||
return i;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NO_GTK
|
||||
if (I_GtkAvailable())
|
||||
{
|
||||
return I_PickIWad_Gtk (wads, numwads, showwin, defaultiwad);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
return I_PickIWad_Cocoa (wads, numwads, showwin, defaultiwad);
|
||||
#endif
|
||||
|
||||
if (!isatty(fileno(stdin)))
|
||||
{
|
||||
return defaultiwad;
|
||||
}
|
||||
|
||||
printf ("Please select a game wad (or 0 to exit):\n");
|
||||
for (i = 0; i < numwads; ++i)
|
||||
{
|
||||
const char *filepart = strrchr (wads[i].Path, '/');
|
||||
if (filepart == NULL)
|
||||
filepart = wads[i].Path;
|
||||
else
|
||||
filepart++;
|
||||
printf ("%d. %s (%s)\n", i+1, wads[i].Name.GetChars(), filepart);
|
||||
}
|
||||
printf ("Which one? ");
|
||||
if (scanf ("%d", &i) != 1 || i > numwads)
|
||||
return -1;
|
||||
return i-1;
|
||||
}
|
||||
|
||||
bool I_WriteIniFailed ()
|
||||
{
|
||||
printf ("The config file %s could not be saved:\n%s\n", GameConfig->GetPathName(), strerror(errno));
|
||||
return false;
|
||||
// return true to retry
|
||||
}
|
||||
|
||||
static const char *pattern;
|
||||
|
||||
#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED < 1080
|
||||
static int matchfile (struct dirent *ent)
|
||||
#else
|
||||
static int matchfile (const struct dirent *ent)
|
||||
#endif
|
||||
{
|
||||
return fnmatch (pattern, ent->d_name, FNM_NOESCAPE) == 0;
|
||||
}
|
||||
|
||||
void *I_FindFirst (const char *filespec, findstate_t *fileinfo)
|
||||
{
|
||||
FString dir;
|
||||
|
||||
const char *slash = strrchr (filespec, '/');
|
||||
if (slash)
|
||||
{
|
||||
pattern = slash+1;
|
||||
dir = FString(filespec, slash-filespec+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
pattern = filespec;
|
||||
dir = ".";
|
||||
}
|
||||
|
||||
fileinfo->current = 0;
|
||||
fileinfo->count = scandir (dir.GetChars(), &fileinfo->namelist,
|
||||
matchfile, alphasort);
|
||||
if (fileinfo->count > 0)
|
||||
{
|
||||
return fileinfo;
|
||||
}
|
||||
return (void*)-1;
|
||||
}
|
||||
|
||||
int I_FindNext (void *handle, findstate_t *fileinfo)
|
||||
{
|
||||
findstate_t *state = (findstate_t *)handle;
|
||||
if (state->current < fileinfo->count)
|
||||
{
|
||||
return ++state->current < fileinfo->count ? 0 : -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int I_FindClose (void *handle)
|
||||
{
|
||||
findstate_t *state = (findstate_t *)handle;
|
||||
if (handle != (void*)-1 && state->count > 0)
|
||||
{
|
||||
for(int i = 0;i < state->count;++i)
|
||||
free (state->namelist[i]);
|
||||
state->count = 0;
|
||||
free (state->namelist);
|
||||
state->namelist = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int I_FindAttr(findstate_t* const fileinfo)
|
||||
{
|
||||
dirent* const ent = fileinfo->namelist[fileinfo->current];
|
||||
bool isdir;
|
||||
|
||||
if (DirEntryExists(ent->d_name, &isdir))
|
||||
{
|
||||
return isdir ? FA_DIREC : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void I_PutInClipboard (const char *str)
|
||||
{
|
||||
SDL_SetClipboardText(str);
|
||||
}
|
||||
|
||||
FString I_GetFromClipboard (bool use_primary_selection)
|
||||
{
|
||||
if(char *ret = SDL_GetClipboardText())
|
||||
{
|
||||
FString text(ret);
|
||||
SDL_free(ret);
|
||||
return text;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Return a random seed, preferably one with lots of entropy.
|
||||
unsigned int I_MakeRNGSeed()
|
||||
{
|
||||
unsigned int seed;
|
||||
int file;
|
||||
|
||||
// Try reading from /dev/urandom first, then /dev/random, then
|
||||
// if all else fails, use a crappy seed from time().
|
||||
seed = time(NULL);
|
||||
file = open("/dev/urandom", O_RDONLY);
|
||||
if (file < 0)
|
||||
{
|
||||
file = open("/dev/random", O_RDONLY);
|
||||
}
|
||||
if (file >= 0)
|
||||
{
|
||||
read(file, &seed, sizeof(seed));
|
||||
close(file);
|
||||
}
|
||||
return seed;
|
||||
}
|
||||
|
||||
TArray<FString> I_GetGogPaths()
|
||||
{
|
||||
// GOG's Doom games are Windows only at the moment
|
||||
return TArray<FString>();
|
||||
}
|
||||
|
19
source/platform/posix/sdl/i_system.mm
Normal file
19
source/platform/posix/sdl/i_system.mm
Normal file
|
@ -0,0 +1,19 @@
|
|||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include "SDL.h"
|
||||
|
||||
void Mac_I_FatalError(const char* errortext)
|
||||
{
|
||||
// Close window or exit fullscreen and release mouse capture
|
||||
SDL_Quit();
|
||||
|
||||
const CFStringRef errorString = CFStringCreateWithCStringNoCopy( kCFAllocatorDefault,
|
||||
errortext, kCFStringEncodingASCII, kCFAllocatorNull );
|
||||
if ( NULL != errorString )
|
||||
{
|
||||
CFOptionFlags dummy;
|
||||
|
||||
CFUserNotificationDisplayAlert( 0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL,
|
||||
CFSTR( "Fatal Error" ), errorString, CFSTR( "Exit" ), NULL, NULL, &dummy );
|
||||
CFRelease( errorString );
|
||||
}
|
||||
}
|
760
source/platform/posix/sdl/sdlglvideo.cpp
Normal file
760
source/platform/posix/sdl/sdlglvideo.cpp
Normal file
|
@ -0,0 +1,760 @@
|
|||
/*
|
||||
** sdlglvideo.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2005-2016 Christoph Oelckers et.al.
|
||||
** 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
// HEADER FILES ------------------------------------------------------------
|
||||
|
||||
#include "doomtype.h"
|
||||
|
||||
#include "i_module.h"
|
||||
#include "i_system.h"
|
||||
#include "i_video.h"
|
||||
#include "m_argv.h"
|
||||
#include "v_video.h"
|
||||
#include "version.h"
|
||||
#include "c_console.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "s_sound.h"
|
||||
|
||||
#include "hardware.h"
|
||||
#include "gl_sysfb.h"
|
||||
#include "gl_load/gl_system.h"
|
||||
#include "r_defs.h"
|
||||
|
||||
#include "gl/renderer/gl_renderer.h"
|
||||
#include "gl/system/gl_framebuffer.h"
|
||||
#include "gl/shaders/gl_shader.h"
|
||||
|
||||
#ifdef HAVE_VULKAN
|
||||
#include "rendering/vulkan/system/vk_framebuffer.h"
|
||||
#endif
|
||||
|
||||
#include "rendering/polyrenderer/backend/poly_framebuffer.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
// TYPES -------------------------------------------------------------------
|
||||
|
||||
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
||||
|
||||
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
||||
|
||||
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
||||
|
||||
extern IVideo *Video;
|
||||
|
||||
EXTERN_CVAR (Int, vid_adapter)
|
||||
EXTERN_CVAR (Int, vid_displaybits)
|
||||
EXTERN_CVAR (Int, vid_defwidth)
|
||||
EXTERN_CVAR (Int, vid_defheight)
|
||||
EXTERN_CVAR (Int, vid_preferbackend)
|
||||
EXTERN_CVAR (Bool, cl_capfps)
|
||||
|
||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||
|
||||
CUSTOM_CVAR(Bool, gl_debug, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
|
||||
{
|
||||
Printf("This won't take effect until " GAMENAME " is restarted.\n");
|
||||
}
|
||||
CUSTOM_CVAR(Bool, gl_es, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
|
||||
{
|
||||
Printf("This won't take effect until " GAMENAME " is restarted.\n");
|
||||
}
|
||||
|
||||
CVAR(Bool, i_soundinbackground, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
||||
CVAR (Int, vid_adapter, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
||||
CUSTOM_CVAR(String, vid_sdl_render_driver, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
|
||||
{
|
||||
Printf("This won't take effect until " GAMENAME " is restarted.\n");
|
||||
}
|
||||
|
||||
CCMD(vid_list_sdl_render_drivers)
|
||||
{
|
||||
for (int i = 0; i < SDL_GetNumRenderDrivers(); ++i)
|
||||
{
|
||||
SDL_RendererInfo info;
|
||||
if (SDL_GetRenderDriverInfo(i, &info) == 0)
|
||||
Printf("%s\n", info.name);
|
||||
}
|
||||
}
|
||||
|
||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
||||
namespace Priv
|
||||
{
|
||||
FModule library("SDL2");
|
||||
|
||||
#define SDL2_OPTIONAL_FUNCTION(RESULT, NAME, ...) \
|
||||
static TOptProc<library, RESULT(*)(__VA_ARGS__)> NAME("SDL_" #NAME)
|
||||
|
||||
SDL2_OPTIONAL_FUNCTION(int, GetWindowBordersSize, SDL_Window *window, int *top, int *left, int *bottom, int *right);
|
||||
#ifdef HAVE_VULKAN
|
||||
SDL2_OPTIONAL_FUNCTION(void, Vulkan_GetDrawableSize, SDL_Window *window, int *width, int *height);
|
||||
SDL2_OPTIONAL_FUNCTION(SDL_bool, Vulkan_GetInstanceExtensions, SDL_Window *window, unsigned int *count, const char **names);
|
||||
SDL2_OPTIONAL_FUNCTION(SDL_bool, Vulkan_CreateSurface, SDL_Window *window, VkInstance instance, VkSurfaceKHR *surface);
|
||||
#endif
|
||||
|
||||
#undef SDL2_OPTIONAL_FUNCTION
|
||||
|
||||
static const uint32_t VulkanWindowFlag = 0x1000'0000;
|
||||
|
||||
SDL_Window *window;
|
||||
bool vulkanEnabled;
|
||||
bool softpolyEnabled;
|
||||
bool fullscreenSwitch;
|
||||
|
||||
void CreateWindow(uint32_t extraFlags)
|
||||
{
|
||||
assert(Priv::window == nullptr);
|
||||
|
||||
// Set default size
|
||||
SDL_Rect bounds;
|
||||
SDL_GetDisplayBounds(vid_adapter, &bounds);
|
||||
|
||||
if (win_w <= 0 || win_h <= 0)
|
||||
{
|
||||
win_w = bounds.w * 8 / 10;
|
||||
win_h = bounds.h * 8 / 10;
|
||||
}
|
||||
|
||||
FString caption;
|
||||
caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime());
|
||||
|
||||
const uint32_t windowFlags = (win_maximized ? SDL_WINDOW_MAXIMIZED : 0) | SDL_WINDOW_RESIZABLE | extraFlags;
|
||||
Priv::window = SDL_CreateWindow(caption,
|
||||
(win_x <= 0) ? SDL_WINDOWPOS_CENTERED_DISPLAY(vid_adapter) : win_x,
|
||||
(win_y <= 0) ? SDL_WINDOWPOS_CENTERED_DISPLAY(vid_adapter) : win_y,
|
||||
win_w, win_h, windowFlags);
|
||||
|
||||
if (Priv::window != nullptr)
|
||||
{
|
||||
// Enforce minimum size limit
|
||||
SDL_SetWindowMinimumSize(Priv::window, VID_MIN_WIDTH, VID_MIN_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyWindow()
|
||||
{
|
||||
assert(Priv::window != nullptr);
|
||||
|
||||
SDL_DestroyWindow(Priv::window);
|
||||
Priv::window = nullptr;
|
||||
}
|
||||
|
||||
void SetupPixelFormat(int multisample, const int *glver)
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
if (multisample > 0) {
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, multisample);
|
||||
}
|
||||
if (gl_debug)
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
|
||||
|
||||
if (gl_es)
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
}
|
||||
else if (glver[0] > 2)
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, glver[0]);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, glver[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SDLVideo : public IVideo
|
||||
{
|
||||
public:
|
||||
SDLVideo ();
|
||||
~SDLVideo ();
|
||||
|
||||
DFrameBuffer *CreateFrameBuffer ();
|
||||
|
||||
private:
|
||||
#ifdef HAVE_VULKAN
|
||||
VulkanDevice *device = nullptr;
|
||||
#endif
|
||||
};
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
#ifdef HAVE_VULKAN
|
||||
void I_GetVulkanDrawableSize(int *width, int *height)
|
||||
{
|
||||
assert(Priv::vulkanEnabled);
|
||||
assert(Priv::window != nullptr);
|
||||
assert(Priv::Vulkan_GetDrawableSize);
|
||||
Priv::Vulkan_GetDrawableSize(Priv::window, width, height);
|
||||
}
|
||||
|
||||
bool I_GetVulkanPlatformExtensions(unsigned int *count, const char **names)
|
||||
{
|
||||
assert(Priv::vulkanEnabled);
|
||||
assert(Priv::window != nullptr);
|
||||
return Priv::Vulkan_GetInstanceExtensions(Priv::window, count, names) == SDL_TRUE;
|
||||
}
|
||||
|
||||
bool I_CreateVulkanSurface(VkInstance instance, VkSurfaceKHR *surface)
|
||||
{
|
||||
assert(Priv::vulkanEnabled);
|
||||
assert(Priv::window != nullptr);
|
||||
return Priv::Vulkan_CreateSurface(Priv::window, instance, surface) == SDL_TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
SDL_Renderer* polyrendertarget = nullptr;
|
||||
SDL_Texture* polytexture = nullptr;
|
||||
int polytexturew = 0;
|
||||
int polytextureh = 0;
|
||||
bool polyvsync = false;
|
||||
bool polyfirstinit = true;
|
||||
}
|
||||
|
||||
void I_PolyPresentInit()
|
||||
{
|
||||
assert(Priv::softpolyEnabled);
|
||||
assert(Priv::window != nullptr);
|
||||
|
||||
if (strcmp(vid_sdl_render_driver, "") != 0)
|
||||
{
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, vid_sdl_render_driver);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *I_PolyPresentLock(int w, int h, bool vsync, int &pitch)
|
||||
{
|
||||
// When vsync changes we need to reinitialize
|
||||
if (polyrendertarget && polyvsync != vsync)
|
||||
{
|
||||
I_PolyPresentDeinit();
|
||||
}
|
||||
|
||||
if (!polyrendertarget)
|
||||
{
|
||||
polyvsync = vsync;
|
||||
|
||||
polyrendertarget = SDL_CreateRenderer(Priv::window, -1, vsync ? SDL_RENDERER_PRESENTVSYNC : 0);
|
||||
if (!polyrendertarget)
|
||||
{
|
||||
I_FatalError("Could not create render target for softpoly: %s\n", SDL_GetError());
|
||||
}
|
||||
|
||||
// Tell the user which render driver is being used, but don't repeat
|
||||
// outselves if we're just changing vsync.
|
||||
if (polyfirstinit)
|
||||
{
|
||||
polyfirstinit = false;
|
||||
|
||||
SDL_RendererInfo rendererInfo;
|
||||
if (SDL_GetRendererInfo(polyrendertarget, &rendererInfo) == 0)
|
||||
{
|
||||
Printf("Using render driver %s\n", rendererInfo.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf("Failed to query render driver\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Mask color
|
||||
SDL_SetRenderDrawColor(polyrendertarget, 0, 0, 0, 255);
|
||||
}
|
||||
|
||||
if (!polytexture || polytexturew != w || polytextureh != h)
|
||||
{
|
||||
if (polytexture)
|
||||
{
|
||||
SDL_DestroyTexture(polytexture);
|
||||
polytexture = nullptr;
|
||||
polytexturew = polytextureh = 0;
|
||||
}
|
||||
if ((polytexture = SDL_CreateTexture(polyrendertarget, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, w, h)) == nullptr)
|
||||
I_Error("Failed to create %dx%d render target texture.", w, h);
|
||||
polytexturew = w;
|
||||
polytextureh = h;
|
||||
}
|
||||
|
||||
uint8_t* pixels;
|
||||
SDL_LockTexture(polytexture, nullptr, (void**)&pixels, &pitch);
|
||||
return pixels;
|
||||
}
|
||||
|
||||
void I_PolyPresentUnlock(int x, int y, int width, int height)
|
||||
{
|
||||
SDL_UnlockTexture(polytexture);
|
||||
|
||||
int ClientWidth, ClientHeight;
|
||||
SDL_GetRendererOutputSize(polyrendertarget, &ClientWidth, &ClientHeight);
|
||||
|
||||
SDL_Rect clearrects[4];
|
||||
int count = 0;
|
||||
if (y > 0)
|
||||
{
|
||||
clearrects[count].x = 0;
|
||||
clearrects[count].y = 0;
|
||||
clearrects[count].w = ClientWidth;
|
||||
clearrects[count].h = y;
|
||||
count++;
|
||||
}
|
||||
if (y + height < ClientHeight)
|
||||
{
|
||||
clearrects[count].x = 0;
|
||||
clearrects[count].y = y + height;
|
||||
clearrects[count].w = ClientWidth;
|
||||
clearrects[count].h = ClientHeight - clearrects[count].y;
|
||||
count++;
|
||||
}
|
||||
if (x > 0)
|
||||
{
|
||||
clearrects[count].x = 0;
|
||||
clearrects[count].y = y;
|
||||
clearrects[count].w = x;
|
||||
clearrects[count].h = height;
|
||||
count++;
|
||||
}
|
||||
if (x + width < ClientWidth)
|
||||
{
|
||||
clearrects[count].x = x + width;
|
||||
clearrects[count].y = y;
|
||||
clearrects[count].w = ClientWidth - clearrects[count].x;
|
||||
clearrects[count].h = height;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
SDL_RenderFillRects(polyrendertarget, clearrects, count);
|
||||
|
||||
SDL_Rect dstrect;
|
||||
dstrect.x = x;
|
||||
dstrect.y = y;
|
||||
dstrect.w = width;
|
||||
dstrect.h = height;
|
||||
SDL_RenderCopy(polyrendertarget, polytexture, nullptr, &dstrect);
|
||||
|
||||
SDL_RenderPresent(polyrendertarget);
|
||||
}
|
||||
|
||||
void I_PolyPresentDeinit()
|
||||
{
|
||||
if (polytexture)
|
||||
{
|
||||
SDL_DestroyTexture(polytexture);
|
||||
polytexture = nullptr;
|
||||
}
|
||||
|
||||
if (polyrendertarget)
|
||||
{
|
||||
SDL_DestroyRenderer(polyrendertarget);
|
||||
polyrendertarget = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
SDLVideo::SDLVideo ()
|
||||
{
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0)
|
||||
{
|
||||
fprintf(stderr, "Video initialization failed: %s\n", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Load optional SDL functions
|
||||
if (!Priv::library.IsLoaded())
|
||||
{
|
||||
Priv::library.Load({ "libSDL2-2.0.so.0", "libSDL2-2.0.so", "libSDL2.so" });
|
||||
}
|
||||
|
||||
#ifdef HAVE_VULKAN
|
||||
Priv::vulkanEnabled = vid_preferbackend == 1
|
||||
&& Priv::Vulkan_GetDrawableSize && Priv::Vulkan_GetInstanceExtensions && Priv::Vulkan_CreateSurface;
|
||||
Priv::softpolyEnabled = vid_preferbackend == 2;
|
||||
|
||||
if (Priv::vulkanEnabled)
|
||||
{
|
||||
Priv::CreateWindow(Priv::VulkanWindowFlag | SDL_WINDOW_HIDDEN);
|
||||
|
||||
if (Priv::window == nullptr)
|
||||
{
|
||||
Priv::vulkanEnabled = false;
|
||||
}
|
||||
}
|
||||
else if (Priv::softpolyEnabled)
|
||||
{
|
||||
Priv::CreateWindow(SDL_WINDOW_HIDDEN);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SDLVideo::~SDLVideo ()
|
||||
{
|
||||
#ifdef HAVE_VULKAN
|
||||
delete device;
|
||||
#endif
|
||||
}
|
||||
|
||||
DFrameBuffer *SDLVideo::CreateFrameBuffer ()
|
||||
{
|
||||
SystemBaseFrameBuffer *fb = nullptr;
|
||||
|
||||
// first try Vulkan, if that fails OpenGL
|
||||
#ifdef HAVE_VULKAN
|
||||
if (Priv::vulkanEnabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
assert(device == nullptr);
|
||||
device = new VulkanDevice();
|
||||
fb = new VulkanFrameBuffer(nullptr, fullscreen, device);
|
||||
}
|
||||
catch (CVulkanError const&)
|
||||
{
|
||||
if (Priv::window != nullptr)
|
||||
{
|
||||
Priv::DestroyWindow();
|
||||
}
|
||||
|
||||
Priv::vulkanEnabled = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (Priv::softpolyEnabled)
|
||||
{
|
||||
fb = new PolyFrameBuffer(nullptr, fullscreen);
|
||||
}
|
||||
|
||||
if (fb == nullptr)
|
||||
{
|
||||
fb = new OpenGLRenderer::OpenGLFrameBuffer(0, fullscreen);
|
||||
}
|
||||
|
||||
return fb;
|
||||
}
|
||||
|
||||
|
||||
IVideo *gl_CreateVideo()
|
||||
{
|
||||
return new SDLVideo();
|
||||
}
|
||||
|
||||
|
||||
// FrameBuffer Implementation -----------------------------------------------
|
||||
|
||||
SystemBaseFrameBuffer::SystemBaseFrameBuffer (void *, bool fullscreen)
|
||||
: DFrameBuffer (vid_defwidth, vid_defheight)
|
||||
{
|
||||
if (Priv::window != nullptr)
|
||||
{
|
||||
SDL_SetWindowFullscreen(Priv::window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
SDL_ShowWindow(Priv::window);
|
||||
}
|
||||
}
|
||||
|
||||
int SystemBaseFrameBuffer::GetClientWidth()
|
||||
{
|
||||
int width = 0;
|
||||
|
||||
if (Priv::softpolyEnabled)
|
||||
{
|
||||
if (polyrendertarget)
|
||||
SDL_GetRendererOutputSize(polyrendertarget, &width, nullptr);
|
||||
else
|
||||
SDL_GetWindowSize(Priv::window, &width, nullptr);
|
||||
return width;
|
||||
}
|
||||
|
||||
#ifdef HAVE_VULKAN
|
||||
assert(Priv::vulkanEnabled);
|
||||
Priv::Vulkan_GetDrawableSize(Priv::window, &width, nullptr);
|
||||
#endif
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
int SystemBaseFrameBuffer::GetClientHeight()
|
||||
{
|
||||
int height = 0;
|
||||
|
||||
if (Priv::softpolyEnabled)
|
||||
{
|
||||
if (polyrendertarget)
|
||||
SDL_GetRendererOutputSize(polyrendertarget, nullptr, &height);
|
||||
else
|
||||
SDL_GetWindowSize(Priv::window, nullptr, &height);
|
||||
return height;
|
||||
}
|
||||
|
||||
#ifdef HAVE_VULKAN
|
||||
assert(Priv::vulkanEnabled);
|
||||
Priv::Vulkan_GetDrawableSize(Priv::window, nullptr, &height);
|
||||
#endif
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
bool SystemBaseFrameBuffer::IsFullscreen ()
|
||||
{
|
||||
return (SDL_GetWindowFlags(Priv::window) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
|
||||
}
|
||||
|
||||
void SystemBaseFrameBuffer::ToggleFullscreen(bool yes)
|
||||
{
|
||||
SDL_SetWindowFullscreen(Priv::window, yes ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
if ( !yes )
|
||||
{
|
||||
if ( !Priv::fullscreenSwitch )
|
||||
{
|
||||
Priv::fullscreenSwitch = true;
|
||||
fullscreen = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Priv::fullscreenSwitch = false;
|
||||
SetWindowSize(win_w, win_h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SystemBaseFrameBuffer::SetWindowSize(int w, int h)
|
||||
{
|
||||
if (w < VID_MIN_WIDTH || h < VID_MIN_HEIGHT)
|
||||
{
|
||||
w = VID_MIN_WIDTH;
|
||||
h = VID_MIN_HEIGHT;
|
||||
}
|
||||
win_w = w;
|
||||
win_h = h;
|
||||
if ( fullscreen )
|
||||
{
|
||||
fullscreen = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
win_maximized = false;
|
||||
SDL_SetWindowSize(Priv::window, w, h);
|
||||
SDL_SetWindowPosition(Priv::window, SDL_WINDOWPOS_CENTERED_DISPLAY(vid_adapter), SDL_WINDOWPOS_CENTERED_DISPLAY(vid_adapter));
|
||||
SetSize(GetClientWidth(), GetClientHeight());
|
||||
int x, y;
|
||||
SDL_GetWindowPosition(Priv::window, &x, &y);
|
||||
win_x = x;
|
||||
win_y = y;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SystemGLFrameBuffer::SystemGLFrameBuffer(void *hMonitor, bool fullscreen)
|
||||
: SystemBaseFrameBuffer(hMonitor, fullscreen)
|
||||
{
|
||||
// NOTE: Core profiles were added with GL 3.2, so there's no sense trying
|
||||
// to set core 3.1 or 3.0. We could try a forward-compatible context
|
||||
// instead, but that would be too restrictive (w.r.t. shaders).
|
||||
static const int glvers[][2] = {
|
||||
{ 4, 6 }, { 4, 5 }, { 4, 4 }, { 4, 3 }, { 4, 2 }, { 4, 1 }, { 4, 0 },
|
||||
{ 3, 3 }, { 3, 2 }, { 2, 0 },
|
||||
{ 0, 0 },
|
||||
};
|
||||
int glveridx = 0;
|
||||
int i;
|
||||
|
||||
const char *version = Args->CheckValue("-glversion");
|
||||
if (version != NULL)
|
||||
{
|
||||
double gl_version = strtod(version, NULL) + 0.01;
|
||||
int vermaj = (int)gl_version;
|
||||
int vermin = (int)(gl_version*10.0) % 10;
|
||||
|
||||
while (glvers[glveridx][0] > vermaj || (glvers[glveridx][0] == vermaj &&
|
||||
glvers[glveridx][1] > vermin))
|
||||
{
|
||||
glveridx++;
|
||||
if (glvers[glveridx][0] == 0)
|
||||
{
|
||||
glveridx = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( ; glvers[glveridx][0] > 0; ++glveridx)
|
||||
{
|
||||
Priv::SetupPixelFormat(0, glvers[glveridx]);
|
||||
Priv::CreateWindow(SDL_WINDOW_OPENGL | (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
|
||||
|
||||
if (Priv::window == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GLContext = SDL_GL_CreateContext(Priv::window);
|
||||
if (GLContext == nullptr)
|
||||
{
|
||||
Priv::DestroyWindow();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SystemGLFrameBuffer::~SystemGLFrameBuffer ()
|
||||
{
|
||||
if (Priv::window)
|
||||
{
|
||||
if (GLContext)
|
||||
{
|
||||
SDL_GL_DeleteContext(GLContext);
|
||||
}
|
||||
|
||||
Priv::DestroyWindow();
|
||||
}
|
||||
}
|
||||
|
||||
int SystemGLFrameBuffer::GetClientWidth()
|
||||
{
|
||||
int width = 0;
|
||||
SDL_GL_GetDrawableSize(Priv::window, &width, nullptr);
|
||||
return width;
|
||||
}
|
||||
|
||||
int SystemGLFrameBuffer::GetClientHeight()
|
||||
{
|
||||
int height = 0;
|
||||
SDL_GL_GetDrawableSize(Priv::window, nullptr, &height);
|
||||
return height;
|
||||
}
|
||||
|
||||
void SystemGLFrameBuffer::SetVSync( bool vsync )
|
||||
{
|
||||
#if defined (__APPLE__)
|
||||
const GLint value = vsync ? 1 : 0;
|
||||
CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval, &value );
|
||||
#else
|
||||
if (vsync)
|
||||
{
|
||||
if (SDL_GL_SetSwapInterval(-1) == -1)
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SystemGLFrameBuffer::SwapBuffers()
|
||||
{
|
||||
SDL_GL_SwapWindow(Priv::window);
|
||||
}
|
||||
|
||||
|
||||
void ProcessSDLWindowEvent(const SDL_WindowEvent &event)
|
||||
{
|
||||
switch (event.event)
|
||||
{
|
||||
extern bool AppActive;
|
||||
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
S_SetSoundPaused(1);
|
||||
AppActive = true;
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
S_SetSoundPaused(i_soundinbackground);
|
||||
AppActive = false;
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_MOVED:
|
||||
if (!fullscreen && Priv::GetWindowBordersSize)
|
||||
{
|
||||
int top = 0, left = 0;
|
||||
Priv::GetWindowBordersSize(Priv::window, &top, &left, nullptr, nullptr);
|
||||
win_x = event.data1-left;
|
||||
win_y = event.data2-top;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
if (!fullscreen && !Priv::fullscreenSwitch)
|
||||
{
|
||||
win_w = event.data1;
|
||||
win_h = event.data2;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_MAXIMIZED:
|
||||
win_maximized = true;
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
win_maximized = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// each platform has its own specific version of this function.
|
||||
void I_SetWindowTitle(const char* caption)
|
||||
{
|
||||
if (caption)
|
||||
{
|
||||
SDL_SetWindowTitle(Priv::window, caption);
|
||||
}
|
||||
else
|
||||
{
|
||||
FString default_caption;
|
||||
default_caption.Format(GAMESIG " %s (%s)", GetVersionString(), GetGitTime());
|
||||
SDL_SetWindowTitle(Priv::window, default_caption);
|
||||
}
|
||||
}
|
||||
|
331
source/platform/posix/sdl/st_start.cpp
Normal file
331
source/platform/posix/sdl/st_start.cpp
Normal file
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
** st_start.cpp
|
||||
** Handles the startup screen.
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2006-2007 Randy Heit
|
||||
** 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
// HEADER FILES ------------------------------------------------------------
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <termios.h>
|
||||
|
||||
#include "st_start.h"
|
||||
#include "doomdef.h"
|
||||
#include "i_system.h"
|
||||
#include "c_cvars.h"
|
||||
#include "doomerrors.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
// TYPES -------------------------------------------------------------------
|
||||
|
||||
class FTTYStartupScreen : public FStartupScreen
|
||||
{
|
||||
public:
|
||||
FTTYStartupScreen(int max_progress);
|
||||
~FTTYStartupScreen();
|
||||
|
||||
void Progress();
|
||||
void NetInit(const char *message, int num_players);
|
||||
void NetProgress(int count);
|
||||
void NetMessage(const char *format, ...); // cover for printf
|
||||
void NetDone();
|
||||
bool NetLoop(bool (*timer_callback)(void *), void *userdata);
|
||||
protected:
|
||||
bool DidNetInit;
|
||||
int NetMaxPos, NetCurPos;
|
||||
const char *TheNetMessage;
|
||||
termios OldTermIOS;
|
||||
};
|
||||
|
||||
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
||||
|
||||
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
||||
|
||||
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
||||
|
||||
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
||||
|
||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||
|
||||
FStartupScreen *StartScreen;
|
||||
|
||||
CUSTOM_CVAR(Int, showendoom, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (self < 0) self = 0;
|
||||
else if (self > 2) self=2;
|
||||
}
|
||||
|
||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
||||
static const char SpinnyProgressChars[4] = { '|', '/', '-', '\\' };
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FStartupScreen :: CreateInstance
|
||||
//
|
||||
// Initializes the startup screen for the detected game.
|
||||
// Sets the size of the progress bar and displays the startup screen.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FStartupScreen *FStartupScreen::CreateInstance(int max_progress)
|
||||
{
|
||||
return new FTTYStartupScreen(max_progress);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FTTYStartupScreen Constructor
|
||||
//
|
||||
// Sets the size of the progress bar and displays the startup screen.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
FTTYStartupScreen::FTTYStartupScreen(int max_progress)
|
||||
: FStartupScreen(max_progress)
|
||||
{
|
||||
DidNetInit = false;
|
||||
NetMaxPos = 0;
|
||||
NetCurPos = 0;
|
||||
TheNetMessage = NULL;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FTTYStartupScreen Destructor
|
||||
//
|
||||
// Called just before entering graphics mode to deconstruct the startup
|
||||
// screen.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
FTTYStartupScreen::~FTTYStartupScreen()
|
||||
{
|
||||
NetDone(); // Just in case it wasn't called yet and needs to be.
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FTTYStartupScreen :: Progress
|
||||
//
|
||||
// If there was a progress bar, this would move it. But the basic TTY
|
||||
// startup screen doesn't have one, so this function does nothing.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FTTYStartupScreen::Progress()
|
||||
{
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FTTYStartupScreen :: NetInit
|
||||
//
|
||||
// Sets stdin for unbuffered I/O, displays the given message, and shows
|
||||
// a progress meter.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FTTYStartupScreen::NetInit(const char *message, int numplayers)
|
||||
{
|
||||
if (!DidNetInit)
|
||||
{
|
||||
termios rawtermios;
|
||||
|
||||
fprintf (stderr, "Press 'Q' to abort network game synchronization.");
|
||||
// Set stdin to raw mode so we can get keypresses in ST_CheckNetAbort()
|
||||
// immediately without waiting for an EOL.
|
||||
tcgetattr (STDIN_FILENO, &OldTermIOS);
|
||||
rawtermios = OldTermIOS;
|
||||
rawtermios.c_lflag &= ~(ICANON | ECHO);
|
||||
tcsetattr (STDIN_FILENO, TCSANOW, &rawtermios);
|
||||
DidNetInit = true;
|
||||
}
|
||||
if (numplayers == 1)
|
||||
{
|
||||
// Status message without any real progress info.
|
||||
fprintf (stderr, "\n%s.", message);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "\n%s: ", message);
|
||||
}
|
||||
fflush (stderr);
|
||||
TheNetMessage = message;
|
||||
NetMaxPos = numplayers;
|
||||
NetCurPos = 0;
|
||||
NetProgress(1); // You always know about yourself
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FTTYStartupScreen :: NetDone
|
||||
//
|
||||
// Restores the old stdin tty settings.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FTTYStartupScreen::NetDone()
|
||||
{
|
||||
// Restore stdin settings
|
||||
if (DidNetInit)
|
||||
{
|
||||
tcsetattr (STDIN_FILENO, TCSANOW, &OldTermIOS);
|
||||
printf ("\n");
|
||||
DidNetInit = false;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FTTYStartupScreen :: NetMessage
|
||||
//
|
||||
// Call this between NetInit() and NetDone() instead of Printf() to
|
||||
// display messages, because the progress meter is mixed in the same output
|
||||
// stream as normal messages.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FTTYStartupScreen::NetMessage(const char *format, ...)
|
||||
{
|
||||
FString str;
|
||||
va_list argptr;
|
||||
|
||||
va_start (argptr, format);
|
||||
str.VFormat (format, argptr);
|
||||
va_end (argptr);
|
||||
fprintf (stderr, "\r%-40s\n", str.GetChars());
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FTTYStartupScreen :: NetProgress
|
||||
//
|
||||
// Sets the network progress meter. If count is 0, it gets bumped by 1.
|
||||
// Otherwise, it is set to count.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FTTYStartupScreen::NetProgress(int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
NetCurPos++;
|
||||
}
|
||||
else if (count > 0)
|
||||
{
|
||||
NetCurPos = count;
|
||||
}
|
||||
if (NetMaxPos == 0)
|
||||
{
|
||||
// Spinny-type progress meter, because we're a guest waiting for the host.
|
||||
fprintf (stderr, "\r%s: %c", TheNetMessage, SpinnyProgressChars[NetCurPos & 3]);
|
||||
fflush (stderr);
|
||||
}
|
||||
else if (NetMaxPos > 1)
|
||||
{
|
||||
// Dotty-type progress meter.
|
||||
fprintf (stderr, "\r%s: ", TheNetMessage);
|
||||
for (i = 0; i < NetCurPos; ++i)
|
||||
{
|
||||
fputc ('.', stderr);
|
||||
}
|
||||
fprintf (stderr, "%*c[%2d/%2d]", NetMaxPos + 1 - NetCurPos, ' ', NetCurPos, NetMaxPos);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FTTYStartupScreen :: NetLoop
|
||||
//
|
||||
// The timer_callback function is called at least two times per second
|
||||
// and passed the userdata value. It should return true to stop the loop and
|
||||
// return control to the caller or false to continue the loop.
|
||||
//
|
||||
// ST_NetLoop will return true if the loop was halted by the callback and
|
||||
// false if the loop was halted because the user wants to abort the
|
||||
// network synchronization.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
bool FTTYStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata)
|
||||
{
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
int retval;
|
||||
char k;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
// Don't flood the network with packets on startup.
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 500000;
|
||||
|
||||
FD_ZERO (&rfds);
|
||||
FD_SET (STDIN_FILENO, &rfds);
|
||||
|
||||
retval = select (1, &rfds, NULL, NULL, &tv);
|
||||
|
||||
if (retval == -1)
|
||||
{
|
||||
// Error
|
||||
}
|
||||
else if (retval == 0)
|
||||
{
|
||||
if (timer_callback (userdata))
|
||||
{
|
||||
fputc ('\n', stderr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (read (STDIN_FILENO, &k, 1) == 1)
|
||||
{
|
||||
// Check input on stdin
|
||||
if (k == 'q' || k == 'Q')
|
||||
{
|
||||
fprintf (stderr, "\nNetwork game synchronization aborted.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ST_Endoom()
|
||||
{
|
||||
throw CExitEvent(0);
|
||||
}
|
444
source/platform/posix/unix/gtk_dialogs.cpp
Normal file
444
source/platform/posix/unix/gtk_dialogs.cpp
Normal file
|
@ -0,0 +1,444 @@
|
|||
/*
|
||||
** gtk_dialogs.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2008-2016 Braden Obrzut
|
||||
** 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef NO_GTK
|
||||
|
||||
#if !DYN_GTK
|
||||
// Function addresses will never be NULL, but that's because we're using the
|
||||
// same code for both dynamic and static.
|
||||
#pragma GCC diagnostic ignored "-Waddress"
|
||||
#endif
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#if GTK_MAJOR_VERSION >= 3
|
||||
#include <gdk/gdk.h>
|
||||
#else
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
typedef enum
|
||||
{
|
||||
GTK_ALIGN_FULL,
|
||||
GTK_ALIGN_START,
|
||||
GTK_ALIGN_END,
|
||||
GTK_ALIGN_CENTER,
|
||||
GTK_ALIGN_BASELINE
|
||||
} GtkAlign;
|
||||
#endif
|
||||
|
||||
#include "c_cvars.h"
|
||||
#include "d_main.h"
|
||||
#include "i_module.h"
|
||||
#include "i_system.h"
|
||||
#include "version.h"
|
||||
|
||||
EXTERN_CVAR (Bool, queryiwad);
|
||||
|
||||
namespace Gtk {
|
||||
|
||||
FModuleMaybe<DYN_GTK> GtkModule{"GTK"};
|
||||
static int GtkAvailable = -1;
|
||||
|
||||
#define DYN_GTK_SYM(x) const FModuleMaybe<DYN_GTK>::Req<GtkModule, decltype(::x)*, &x> x{#x};
|
||||
#define DYN_GTK_REQ_SYM(x, proto) const FModuleMaybe<DYN_GTK>::Req<GtkModule, proto, &x> x{#x};
|
||||
#if GTK_MAJOR_VERSION >= 3
|
||||
#define DYN_GTK_OPT2_SYM(x, proto) const FModuleMaybe<DYN_GTK>::Opt<GtkModule, proto, nullptr> x{#x};
|
||||
#define DYN_GTK_OPT3_SYM(x, proto) const FModuleMaybe<DYN_GTK>::Opt<GtkModule, proto, &x> x{#x};
|
||||
#else
|
||||
#define DYN_GTK_OPT2_SYM(x, proto) const FModuleMaybe<DYN_GTK>::Opt<GtkModule, proto, &x> x{#x};
|
||||
#define DYN_GTK_OPT3_SYM(x, proto) const FModuleMaybe<DYN_GTK>::Opt<GtkModule, proto, nullptr> x{#x};
|
||||
#endif
|
||||
|
||||
DYN_GTK_SYM(g_main_context_iteration);
|
||||
DYN_GTK_SYM(g_signal_connect_data);
|
||||
DYN_GTK_SYM(g_type_check_instance_cast);
|
||||
DYN_GTK_SYM(g_type_check_instance_is_a);
|
||||
DYN_GTK_SYM(g_value_get_int);
|
||||
DYN_GTK_SYM(g_value_unset);
|
||||
DYN_GTK_SYM(gtk_box_get_type);
|
||||
DYN_GTK_SYM(gtk_box_pack_end);
|
||||
DYN_GTK_SYM(gtk_box_pack_start);
|
||||
DYN_GTK_SYM(gtk_box_set_spacing);
|
||||
DYN_GTK_SYM(gtk_button_box_get_type);
|
||||
DYN_GTK_SYM(gtk_button_box_set_layout);
|
||||
DYN_GTK_SYM(gtk_button_new_with_label);
|
||||
DYN_GTK_SYM(gtk_cell_renderer_text_new);
|
||||
DYN_GTK_SYM(gtk_check_button_new_with_label);
|
||||
DYN_GTK_SYM(gtk_container_add);
|
||||
DYN_GTK_SYM(gtk_container_get_type);
|
||||
DYN_GTK_SYM(gtk_container_set_border_width);
|
||||
DYN_GTK_SYM(gtk_init_check);
|
||||
DYN_GTK_SYM(gtk_label_new);
|
||||
DYN_GTK_SYM(gtk_list_store_append);
|
||||
DYN_GTK_SYM(gtk_list_store_new);
|
||||
DYN_GTK_SYM(gtk_list_store_set);
|
||||
DYN_GTK_SYM(gtk_toggle_button_get_type);
|
||||
DYN_GTK_SYM(gtk_toggle_button_set_active);
|
||||
DYN_GTK_SYM(gtk_tree_model_get_type);
|
||||
DYN_GTK_SYM(gtk_tree_model_get_value);
|
||||
DYN_GTK_SYM(gtk_tree_selection_get_selected);
|
||||
DYN_GTK_SYM(gtk_tree_selection_select_iter);
|
||||
DYN_GTK_SYM(gtk_tree_view_append_column);
|
||||
// Explicitly give the type so that attributes don't cause a warning.
|
||||
DYN_GTK_REQ_SYM(gtk_tree_view_column_new_with_attributes, GtkTreeViewColumn *(*)(const gchar *, GtkCellRenderer *, ...));
|
||||
DYN_GTK_SYM(gtk_toggle_button_get_active);
|
||||
DYN_GTK_SYM(gtk_tree_view_get_selection);
|
||||
DYN_GTK_SYM(gtk_tree_view_get_type);
|
||||
DYN_GTK_SYM(gtk_tree_view_new_with_model);
|
||||
DYN_GTK_SYM(gtk_main);
|
||||
DYN_GTK_SYM(gtk_main_quit);
|
||||
DYN_GTK_SYM(gtk_widget_destroy);
|
||||
DYN_GTK_SYM(gtk_widget_grab_default);
|
||||
DYN_GTK_SYM(gtk_widget_get_type);
|
||||
DYN_GTK_SYM(gtk_widget_set_can_default);
|
||||
DYN_GTK_SYM(gtk_widget_show_all);
|
||||
DYN_GTK_SYM(gtk_window_activate_default);
|
||||
DYN_GTK_SYM(gtk_window_get_type);
|
||||
DYN_GTK_SYM(gtk_window_new);
|
||||
DYN_GTK_SYM(gtk_window_set_gravity);
|
||||
DYN_GTK_SYM(gtk_window_set_position);
|
||||
DYN_GTK_SYM(gtk_window_set_title);
|
||||
DYN_GTK_SYM(gtk_window_set_resizable);
|
||||
DYN_GTK_SYM(gtk_dialog_run);
|
||||
DYN_GTK_SYM(gtk_dialog_get_type);
|
||||
|
||||
// Gtk3 Only
|
||||
DYN_GTK_OPT3_SYM(gtk_box_new, GtkWidget *(*)(GtkOrientation, gint));
|
||||
DYN_GTK_OPT3_SYM(gtk_button_box_new, GtkWidget *(*)(GtkOrientation));
|
||||
DYN_GTK_OPT3_SYM(gtk_widget_set_halign, void(*)(GtkWidget *, GtkAlign));
|
||||
DYN_GTK_OPT3_SYM(gtk_widget_set_valign, void(*)(GtkWidget *, GtkAlign));
|
||||
DYN_GTK_OPT3_SYM(gtk_message_dialog_new, GtkWidget* (*)(GtkWindow*, GtkDialogFlags, GtkMessageType, GtkButtonsType, const gchar*, ...));
|
||||
|
||||
// Gtk2 Only
|
||||
DYN_GTK_OPT2_SYM(gtk_misc_get_type, GType(*)());
|
||||
DYN_GTK_OPT2_SYM(gtk_hbox_new, GtkWidget *(*)(gboolean, gint));
|
||||
DYN_GTK_OPT2_SYM(gtk_hbutton_box_new, GtkWidget *(*)());
|
||||
DYN_GTK_OPT2_SYM(gtk_misc_set_alignment, void(*)(GtkMisc *, gfloat, gfloat));
|
||||
DYN_GTK_OPT2_SYM(gtk_vbox_new, GtkWidget *(*)(gboolean, gint));
|
||||
|
||||
#undef DYN_GTK_SYM
|
||||
#undef DYN_GTK_REQ_SYM
|
||||
#undef DYN_GTK_OPT2_SYM
|
||||
#undef DYN_GTK_OPT3_SYM
|
||||
|
||||
// GtkTreeViews eats return keys. I want this to be like a Windows listbox
|
||||
// where pressing Return can still activate the default button.
|
||||
static gint AllowDefault(GtkWidget *widget, GdkEventKey *event, gpointer func_data)
|
||||
{
|
||||
if (event->type == GDK_KEY_PRESS && event->keyval == GDK_KEY_Return)
|
||||
{
|
||||
gtk_window_activate_default (GTK_WINDOW(func_data));
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Double-clicking an entry in the list is the same as pressing OK.
|
||||
static gint DoubleClickChecker(GtkWidget *widget, GdkEventButton *event, gpointer func_data)
|
||||
{
|
||||
if (event->type == GDK_2BUTTON_PRESS)
|
||||
{
|
||||
*(int *)func_data = 1;
|
||||
gtk_main_quit();
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// When the user presses escape, that should be the same as canceling the dialog.
|
||||
static gint CheckEscape (GtkWidget *widget, GdkEventKey *event, gpointer func_data)
|
||||
{
|
||||
if (event->type == GDK_KEY_PRESS && event->keyval == GDK_KEY_Escape)
|
||||
{
|
||||
gtk_main_quit();
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void ClickedOK(GtkButton *button, gpointer func_data)
|
||||
{
|
||||
*(int *)func_data = 1;
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
static int PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad)
|
||||
{
|
||||
GtkWidget *window;
|
||||
GtkWidget *vbox = nullptr;
|
||||
GtkWidget *hbox = nullptr;
|
||||
GtkWidget *bbox = nullptr;
|
||||
GtkWidget *widget;
|
||||
GtkWidget *tree;
|
||||
GtkWidget *check;
|
||||
GtkListStore *store;
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeViewColumn *column;
|
||||
GtkTreeSelection *selection;
|
||||
GtkTreeIter iter, defiter;
|
||||
int close_style = 0;
|
||||
int i;
|
||||
char caption[100];
|
||||
|
||||
// Create the dialog window.
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
mysnprintf(caption, countof(caption), GAMESIG " %s: Select an IWAD to use", GetVersionString());
|
||||
gtk_window_set_title (GTK_WINDOW(window), caption);
|
||||
gtk_window_set_position (GTK_WINDOW(window), GTK_WIN_POS_CENTER);
|
||||
gtk_window_set_gravity (GTK_WINDOW(window), GDK_GRAVITY_CENTER);
|
||||
gtk_container_set_border_width (GTK_CONTAINER(window), 10);
|
||||
g_signal_connect (window, "delete_event", G_CALLBACK(gtk_main_quit), NULL);
|
||||
g_signal_connect (window, "key_press_event", G_CALLBACK(CheckEscape), NULL);
|
||||
|
||||
// Create the vbox container.
|
||||
if (gtk_box_new) // Gtk3
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
|
||||
else if (gtk_vbox_new) // Gtk2
|
||||
vbox = gtk_vbox_new (FALSE, 10);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER(window), vbox);
|
||||
|
||||
// Create the top label.
|
||||
widget = gtk_label_new (GAMENAME " found more than one IWAD\nSelect from the list below to determine which one to use:");
|
||||
gtk_box_pack_start (GTK_BOX(vbox), widget, false, false, 0);
|
||||
|
||||
if (gtk_widget_set_halign && gtk_widget_set_valign) // Gtk3
|
||||
{
|
||||
gtk_widget_set_halign (widget, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (widget, GTK_ALIGN_START);
|
||||
}
|
||||
else if (gtk_misc_set_alignment && gtk_misc_get_type) // Gtk2
|
||||
gtk_misc_set_alignment (GTK_MISC(widget), 0, 0);
|
||||
|
||||
// Create a list store with all the found IWADs.
|
||||
store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
|
||||
for (i = 0; i < numwads; ++i)
|
||||
{
|
||||
const char *filepart = strrchr (wads[i].Path, '/');
|
||||
if (filepart == NULL)
|
||||
filepart = wads[i].Path;
|
||||
else
|
||||
filepart++;
|
||||
gtk_list_store_append (store, &iter);
|
||||
gtk_list_store_set (store, &iter,
|
||||
0, filepart,
|
||||
1, wads[i].Name.GetChars(),
|
||||
2, i,
|
||||
-1);
|
||||
if (i == defaultiwad)
|
||||
{
|
||||
defiter = iter;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the tree view control to show the list.
|
||||
tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL(store));
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
column = gtk_tree_view_column_new_with_attributes ("IWAD", renderer, "text", 0, NULL);
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW(tree), column);
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
column = gtk_tree_view_column_new_with_attributes ("Game", renderer, "text", 1, NULL);
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW(tree), column);
|
||||
gtk_box_pack_start (GTK_BOX(vbox), GTK_WIDGET(tree), true, true, 0);
|
||||
g_signal_connect(G_OBJECT(tree), "button_press_event", G_CALLBACK(DoubleClickChecker), &close_style);
|
||||
g_signal_connect(G_OBJECT(tree), "key_press_event", G_CALLBACK(AllowDefault), window);
|
||||
|
||||
// Select the default IWAD.
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(tree));
|
||||
gtk_tree_selection_select_iter (selection, &defiter);
|
||||
|
||||
// Create the hbox for the bottom row.
|
||||
if (gtk_box_new) // Gtk3
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
else if (gtk_hbox_new) // Gtk2
|
||||
hbox = gtk_hbox_new (FALSE, 0);
|
||||
|
||||
gtk_box_pack_end (GTK_BOX(vbox), hbox, false, false, 0);
|
||||
|
||||
// Create the "Don't ask" checkbox.
|
||||
check = gtk_check_button_new_with_label ("Don't ask me this again");
|
||||
gtk_box_pack_start (GTK_BOX(hbox), check, false, false, 0);
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(check), !showwin);
|
||||
|
||||
// Create the OK/Cancel button box.
|
||||
if (gtk_button_box_new) // Gtk3
|
||||
bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
|
||||
else if (gtk_hbutton_box_new) // Gtk2
|
||||
bbox = gtk_hbutton_box_new ();
|
||||
|
||||
gtk_button_box_set_layout (GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
|
||||
gtk_box_set_spacing (GTK_BOX(bbox), 10);
|
||||
gtk_box_pack_end (GTK_BOX(hbox), bbox, false, false, 0);
|
||||
|
||||
// Create the OK button.
|
||||
widget = gtk_button_new_with_label ("OK");
|
||||
|
||||
gtk_box_pack_start (GTK_BOX(bbox), widget, false, false, 0);
|
||||
|
||||
gtk_widget_set_can_default (widget, true);
|
||||
|
||||
gtk_widget_grab_default (widget);
|
||||
g_signal_connect (widget, "clicked", G_CALLBACK(ClickedOK), &close_style);
|
||||
g_signal_connect (widget, "activate", G_CALLBACK(ClickedOK), &close_style);
|
||||
|
||||
// Create the cancel button.
|
||||
widget = gtk_button_new_with_label ("Cancel");
|
||||
|
||||
gtk_box_pack_start (GTK_BOX(bbox), widget, false, false, 0);
|
||||
g_signal_connect (widget, "clicked", G_CALLBACK(gtk_main_quit), &window);
|
||||
|
||||
// Finally we can show everything.
|
||||
gtk_widget_show_all (window);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
if (close_style == 1)
|
||||
{
|
||||
GtkTreeModel *model;
|
||||
GValue value = { 0, { {0} } };
|
||||
|
||||
// Find out which IWAD was selected.
|
||||
gtk_tree_selection_get_selected (selection, &model, &iter);
|
||||
gtk_tree_model_get_value (GTK_TREE_MODEL(model), &iter, 2, &value);
|
||||
i = g_value_get_int (&value);
|
||||
g_value_unset (&value);
|
||||
|
||||
// Set state of queryiwad based on the checkbox.
|
||||
queryiwad = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(check));
|
||||
}
|
||||
else
|
||||
{
|
||||
i = -1;
|
||||
}
|
||||
|
||||
if (GTK_IS_WINDOW(window))
|
||||
{
|
||||
gtk_widget_destroy (window);
|
||||
// If we don't do this, then the X window might not actually disappear.
|
||||
while (g_main_context_iteration (NULL, FALSE)) {}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static void ShowError(const char* errortext)
|
||||
{
|
||||
GtkWidget *window;
|
||||
GtkWidget *widget;
|
||||
GtkWidget *vbox = nullptr;
|
||||
GtkWidget *bbox = nullptr;
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_title (GTK_WINDOW(window), "Fatal error");
|
||||
gtk_window_set_position (GTK_WINDOW(window), GTK_WIN_POS_CENTER);
|
||||
gtk_window_set_gravity (GTK_WINDOW(window), GDK_GRAVITY_CENTER);
|
||||
gtk_window_set_resizable (GTK_WINDOW(window), false);
|
||||
gtk_container_set_border_width (GTK_CONTAINER(window), 10);
|
||||
g_signal_connect (window, "delete_event", G_CALLBACK(gtk_main_quit), NULL);
|
||||
g_signal_connect (window, "key_press_event", G_CALLBACK(CheckEscape), NULL);
|
||||
|
||||
// Create the vbox container.
|
||||
if (gtk_box_new) // Gtk3
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
|
||||
else if (gtk_vbox_new) // Gtk2
|
||||
vbox = gtk_vbox_new (FALSE, 10);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER(window), vbox);
|
||||
|
||||
// Create the label.
|
||||
widget = gtk_label_new ((const gchar *) errortext);
|
||||
gtk_box_pack_start (GTK_BOX(vbox), widget, false, false, 0);
|
||||
|
||||
if (gtk_widget_set_halign && gtk_widget_set_valign) // Gtk3
|
||||
{
|
||||
gtk_widget_set_halign (widget, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (widget, GTK_ALIGN_START);
|
||||
}
|
||||
else if (gtk_misc_set_alignment && gtk_misc_get_type) // Gtk2
|
||||
gtk_misc_set_alignment (GTK_MISC(widget), 0, 0);
|
||||
|
||||
// Create the Exit button box.
|
||||
if (gtk_button_box_new) // Gtk3
|
||||
bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
|
||||
else if (gtk_hbutton_box_new) // Gtk2
|
||||
bbox = gtk_hbutton_box_new ();
|
||||
|
||||
gtk_button_box_set_layout (GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
|
||||
gtk_box_set_spacing (GTK_BOX(bbox), 10);
|
||||
gtk_box_pack_end (GTK_BOX(vbox), bbox, false, false, 0);
|
||||
|
||||
// Create the cancel button.
|
||||
widget = gtk_button_new_with_label ("Exit");
|
||||
gtk_box_pack_start (GTK_BOX(bbox), widget, false, false, 0);
|
||||
g_signal_connect (widget, "clicked", G_CALLBACK(gtk_main_quit), &window);
|
||||
|
||||
// Finally we can show everything.
|
||||
gtk_widget_show_all (window);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
if (GTK_IS_WINDOW(window))
|
||||
{
|
||||
gtk_widget_destroy (window);
|
||||
// If we don't do this, then the X window might not actually disappear.
|
||||
while (g_main_context_iteration (NULL, FALSE)) {}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Gtk
|
||||
|
||||
int I_PickIWad_Gtk (WadStuff *wads, int numwads, bool showwin, int defaultiwad)
|
||||
{
|
||||
return Gtk::PickIWad (wads, numwads, showwin, defaultiwad);
|
||||
}
|
||||
|
||||
void I_ShowFatalError_Gtk(const char* errortext) {
|
||||
Gtk::ShowError(errortext);
|
||||
}
|
||||
|
||||
bool I_GtkAvailable()
|
||||
{
|
||||
using namespace Gtk;
|
||||
|
||||
if(GtkAvailable < 0)
|
||||
{
|
||||
if (!GtkModule.Load({"libgtk-3.so.0", "libgtk-x11-2.0.so.0"}))
|
||||
{
|
||||
GtkAvailable = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
int argc = 0;
|
||||
char **argv = nullptr;
|
||||
GtkAvailable = Gtk::gtk_init_check (&argc, &argv);
|
||||
}
|
||||
|
||||
return GtkAvailable != 0;
|
||||
}
|
||||
|
||||
#endif
|
8613
source/platform/posix/zdoom.xpm
Normal file
8613
source/platform/posix/zdoom.xpm
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,723 +0,0 @@
|
|||
/*
|
||||
** This code is partially original EDuke32 code, partially taken from ZDoom
|
||||
**
|
||||
** For the portions taken from ZDoom the following applies:
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2005-2016 Randy Heit
|
||||
** 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 <SDL.h>
|
||||
|
||||
#include "m_argv.h"
|
||||
|
||||
#include "c_buttons.h"
|
||||
#include "d_event.h"
|
||||
#include "d_gui.h"
|
||||
#include "c_console.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "c_cvars.h"
|
||||
#include "keydef.h"
|
||||
#include "utf8.h"
|
||||
#include "menu/menu.h"
|
||||
|
||||
|
||||
char grabmouse_low(char a);
|
||||
void WindowMoved(int x, int y);
|
||||
extern SDL_Window* sdl_window;
|
||||
|
||||
|
||||
static uint8_t keytranslation[SDL_NUM_SCANCODES];
|
||||
|
||||
void buildkeytranslationtable(void)
|
||||
{
|
||||
memset(keytranslation, 0, sizeof(keytranslation));
|
||||
|
||||
#define MAP(x,y) keytranslation[x] = y
|
||||
MAP(SDL_SCANCODE_BACKSPACE, 0xe);
|
||||
MAP(SDL_SCANCODE_TAB, 0xf);
|
||||
MAP(SDL_SCANCODE_RETURN, 0x1c);
|
||||
MAP(SDL_SCANCODE_PAUSE, 0x59); // 0x1d + 0x45 + 0x9d + 0xc5
|
||||
MAP(SDL_SCANCODE_ESCAPE, 0x1);
|
||||
MAP(SDL_SCANCODE_SPACE, 0x39);
|
||||
MAP(SDL_SCANCODE_COMMA, 0x33);
|
||||
MAP(SDL_SCANCODE_NONUSBACKSLASH, 0x56);
|
||||
MAP(SDL_SCANCODE_MINUS, 0xc);
|
||||
MAP(SDL_SCANCODE_PERIOD, 0x34);
|
||||
MAP(SDL_SCANCODE_SLASH, 0x35);
|
||||
MAP(SDL_SCANCODE_0, 0xb);
|
||||
MAP(SDL_SCANCODE_1, 0x2);
|
||||
MAP(SDL_SCANCODE_2, 0x3);
|
||||
MAP(SDL_SCANCODE_3, 0x4);
|
||||
MAP(SDL_SCANCODE_4, 0x5);
|
||||
MAP(SDL_SCANCODE_5, 0x6);
|
||||
MAP(SDL_SCANCODE_6, 0x7);
|
||||
MAP(SDL_SCANCODE_7, 0x8);
|
||||
MAP(SDL_SCANCODE_8, 0x9);
|
||||
MAP(SDL_SCANCODE_9, 0xa);
|
||||
MAP(SDL_SCANCODE_SEMICOLON, 0x27);
|
||||
MAP(SDL_SCANCODE_APOSTROPHE, 0x28);
|
||||
MAP(SDL_SCANCODE_EQUALS, 0xd);
|
||||
MAP(SDL_SCANCODE_LEFTBRACKET, 0x1a);
|
||||
MAP(SDL_SCANCODE_BACKSLASH, 0x2b);
|
||||
MAP(SDL_SCANCODE_RIGHTBRACKET, 0x1b);
|
||||
MAP(SDL_SCANCODE_A, 0x1e);
|
||||
MAP(SDL_SCANCODE_B, 0x30);
|
||||
MAP(SDL_SCANCODE_C, 0x2e);
|
||||
MAP(SDL_SCANCODE_D, 0x20);
|
||||
MAP(SDL_SCANCODE_E, 0x12);
|
||||
MAP(SDL_SCANCODE_F, 0x21);
|
||||
MAP(SDL_SCANCODE_G, 0x22);
|
||||
MAP(SDL_SCANCODE_H, 0x23);
|
||||
MAP(SDL_SCANCODE_I, 0x17);
|
||||
MAP(SDL_SCANCODE_J, 0x24);
|
||||
MAP(SDL_SCANCODE_K, 0x25);
|
||||
MAP(SDL_SCANCODE_L, 0x26);
|
||||
MAP(SDL_SCANCODE_M, 0x32);
|
||||
MAP(SDL_SCANCODE_N, 0x31);
|
||||
MAP(SDL_SCANCODE_O, 0x18);
|
||||
MAP(SDL_SCANCODE_P, 0x19);
|
||||
MAP(SDL_SCANCODE_Q, 0x10);
|
||||
MAP(SDL_SCANCODE_R, 0x13);
|
||||
MAP(SDL_SCANCODE_S, 0x1f);
|
||||
MAP(SDL_SCANCODE_T, 0x14);
|
||||
MAP(SDL_SCANCODE_U, 0x16);
|
||||
MAP(SDL_SCANCODE_V, 0x2f);
|
||||
MAP(SDL_SCANCODE_W, 0x11);
|
||||
MAP(SDL_SCANCODE_X, 0x2d);
|
||||
MAP(SDL_SCANCODE_Y, 0x15);
|
||||
MAP(SDL_SCANCODE_Z, 0x2c);
|
||||
MAP(SDL_SCANCODE_DELETE, 0xd3);
|
||||
MAP(SDL_SCANCODE_KP_0, 0x52);
|
||||
MAP(SDL_SCANCODE_KP_1, 0x4f);
|
||||
MAP(SDL_SCANCODE_KP_2, 0x50);
|
||||
MAP(SDL_SCANCODE_KP_3, 0x51);
|
||||
MAP(SDL_SCANCODE_KP_4, 0x4b);
|
||||
MAP(SDL_SCANCODE_KP_5, 0x4c);
|
||||
MAP(SDL_SCANCODE_KP_CLEAR, 0x4c);
|
||||
MAP(SDL_SCANCODE_CLEAR, 0x4c);
|
||||
MAP(SDL_SCANCODE_KP_6, 0x4d);
|
||||
MAP(SDL_SCANCODE_KP_7, 0x47);
|
||||
MAP(SDL_SCANCODE_KP_8, 0x48);
|
||||
MAP(SDL_SCANCODE_KP_9, 0x49);
|
||||
MAP(SDL_SCANCODE_KP_PERIOD, 0x53);
|
||||
MAP(SDL_SCANCODE_KP_DIVIDE, 0xb5);
|
||||
MAP(SDL_SCANCODE_KP_MULTIPLY, 0x37);
|
||||
MAP(SDL_SCANCODE_KP_MINUS, 0x4a);
|
||||
MAP(SDL_SCANCODE_KP_PLUS, 0x4e);
|
||||
MAP(SDL_SCANCODE_KP_ENTER, 0x9c);
|
||||
//MAP(SDL_SCANCODE_KP_EQUALS, );
|
||||
MAP(SDL_SCANCODE_UP, 0xc8);
|
||||
MAP(SDL_SCANCODE_DOWN, 0xd0);
|
||||
MAP(SDL_SCANCODE_RIGHT, 0xcd);
|
||||
MAP(SDL_SCANCODE_LEFT, 0xcb);
|
||||
MAP(SDL_SCANCODE_INSERT, 0xd2);
|
||||
MAP(SDL_SCANCODE_HOME, 0xc7);
|
||||
MAP(SDL_SCANCODE_END, 0xcf);
|
||||
MAP(SDL_SCANCODE_PAGEUP, 0xc9);
|
||||
MAP(SDL_SCANCODE_PAGEDOWN, 0xd1);
|
||||
MAP(SDL_SCANCODE_F1, 0x3b);
|
||||
MAP(SDL_SCANCODE_F2, 0x3c);
|
||||
MAP(SDL_SCANCODE_F3, 0x3d);
|
||||
MAP(SDL_SCANCODE_F4, 0x3e);
|
||||
MAP(SDL_SCANCODE_F5, 0x3f);
|
||||
MAP(SDL_SCANCODE_F6, 0x40);
|
||||
MAP(SDL_SCANCODE_F7, 0x41);
|
||||
MAP(SDL_SCANCODE_F8, 0x42);
|
||||
MAP(SDL_SCANCODE_F9, 0x43);
|
||||
MAP(SDL_SCANCODE_F10, 0x44);
|
||||
MAP(SDL_SCANCODE_F11, 0x57);
|
||||
MAP(SDL_SCANCODE_F12, 0x58);
|
||||
MAP(SDL_SCANCODE_NUMLOCKCLEAR, 0x45);
|
||||
MAP(SDL_SCANCODE_CAPSLOCK, 0x3a);
|
||||
MAP(SDL_SCANCODE_SCROLLLOCK, 0x46);
|
||||
MAP(SDL_SCANCODE_RSHIFT, 0x36);
|
||||
MAP(SDL_SCANCODE_LSHIFT, 0x2a);
|
||||
MAP(SDL_SCANCODE_RCTRL, 0x9d);
|
||||
MAP(SDL_SCANCODE_LCTRL, 0x1d);
|
||||
MAP(SDL_SCANCODE_RALT, 0xb8);
|
||||
MAP(SDL_SCANCODE_LALT, 0x38);
|
||||
MAP(SDL_SCANCODE_LGUI, 0xdb); // win l
|
||||
MAP(SDL_SCANCODE_RGUI, 0xdc); // win r
|
||||
// MAP(SDL_SCANCODE_PRINTSCREEN, -2); // 0xaa + 0xb7
|
||||
MAP(SDL_SCANCODE_SYSREQ, 0x54); // alt+printscr
|
||||
// MAP(SDL_SCANCODE_PAUSE, 0xb7); // ctrl+pause
|
||||
MAP(SDL_SCANCODE_MENU, 0xdd); // win menu?
|
||||
MAP(SDL_SCANCODE_GRAVE, 0x29); // tilde
|
||||
#undef MAP
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// initmouse() -- init mouse input
|
||||
//
|
||||
void mouseInit(void)
|
||||
{
|
||||
mouseGrabInput(g_mouseEnabled = g_mouseLockedToWindow); // FIXME - SA
|
||||
}
|
||||
|
||||
//
|
||||
// uninitmouse() -- uninit mouse input
|
||||
//
|
||||
void mouseUninit(void)
|
||||
{
|
||||
mouseGrabInput(0);
|
||||
g_mouseEnabled = 0;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// grabmouse_low() -- show/hide mouse cursor, lower level (doesn't check state).
|
||||
// furthermore return 0 if successful.
|
||||
//
|
||||
|
||||
char grabmouse_low(char a)
|
||||
{
|
||||
/* FIXME: Maybe it's better to make sure that grabmouse_low
|
||||
is called only when a window is ready? */
|
||||
if (sdl_window)
|
||||
SDL_SetWindowGrab(sdl_window, a ? SDL_TRUE : SDL_FALSE);
|
||||
return SDL_SetRelativeMouseMode(a ? SDL_TRUE : SDL_FALSE);
|
||||
}
|
||||
|
||||
//
|
||||
// grabmouse() -- show/hide mouse cursor
|
||||
//
|
||||
void mouseGrabInput(bool grab)
|
||||
{
|
||||
if (appactive && g_mouseEnabled)
|
||||
{
|
||||
if ((grab != g_mouseGrabbed) && !grabmouse_low(grab))
|
||||
g_mouseGrabbed = grab;
|
||||
}
|
||||
else
|
||||
g_mouseGrabbed = grab;
|
||||
|
||||
inputState.MouseSetPos(0, 0);
|
||||
SDL_ShowCursor(!grab ? SDL_ENABLE : SDL_DISABLE);
|
||||
if (grab) GUICapture &= ~1;
|
||||
else GUICapture |= 1;
|
||||
}
|
||||
|
||||
|
||||
// So this is how the engine handles text input?
|
||||
// Argh. This is just gross.
|
||||
int scancodetoasciihack(SDL_Event& ev)
|
||||
{
|
||||
int sc = ev.key.keysym.scancode;
|
||||
SDL_Keycode keyvalue = ev.key.keysym.sym;
|
||||
int code = keytranslation[sc];
|
||||
// Modifiers that have to be held down to be effective
|
||||
// (excludes KMOD_NUM, for example).
|
||||
static const int MODIFIERS =
|
||||
KMOD_LSHIFT | KMOD_RSHIFT | KMOD_LCTRL | KMOD_RCTRL |
|
||||
KMOD_LALT | KMOD_RALT | KMOD_LGUI | KMOD_RGUI;
|
||||
|
||||
// XXX: see osd.c, OSD_HandleChar(), there are more...
|
||||
if (
|
||||
(sc == SDL_SCANCODE_RETURN || sc == SDL_SCANCODE_KP_ENTER ||
|
||||
sc == SDL_SCANCODE_ESCAPE ||
|
||||
sc == SDL_SCANCODE_BACKSPACE ||
|
||||
sc == SDL_SCANCODE_TAB ||
|
||||
(((ev.key.keysym.mod) & MODIFIERS) == KMOD_LCTRL &&
|
||||
(sc >= SDL_SCANCODE_A && sc <= SDL_SCANCODE_Z))))
|
||||
{
|
||||
char keyvalue;
|
||||
switch (sc)
|
||||
{
|
||||
case SDL_SCANCODE_RETURN: case SDL_SCANCODE_KP_ENTER: keyvalue = '\r'; break;
|
||||
case SDL_SCANCODE_ESCAPE: keyvalue = 27; break;
|
||||
case SDL_SCANCODE_BACKSPACE: keyvalue = '\b'; break;
|
||||
case SDL_SCANCODE_TAB: keyvalue = '\t'; break;
|
||||
default: keyvalue = sc - SDL_SCANCODE_A + 1; break; // Ctrl+A --> 1, etc.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Necessary for Duke 3D's method of entering cheats to work without showing IMEs.
|
||||
SDL_TEXTINPUT is preferable overall, but with bitmap fonts it has no advantage.
|
||||
*/
|
||||
// Note that this is not how text input is supposed to be handled!
|
||||
|
||||
if ('a' <= keyvalue && keyvalue <= 'z')
|
||||
{
|
||||
if (!!(ev.key.keysym.mod & KMOD_SHIFT) ^ !!(ev.key.keysym.mod & KMOD_CAPS))
|
||||
keyvalue -= 'a' - 'A';
|
||||
}
|
||||
else if (ev.key.keysym.mod & KMOD_SHIFT)
|
||||
{
|
||||
keyvalue = g_keyAsciiTableShift[code];
|
||||
}
|
||||
else if (ev.key.keysym.mod & KMOD_NUM) // && !(ev.key.keysym.mod & KMOD_SHIFT)
|
||||
{
|
||||
switch (keyvalue)
|
||||
{
|
||||
case SDLK_KP_1: keyvalue = '1'; break;
|
||||
case SDLK_KP_2: keyvalue = '2'; break;
|
||||
case SDLK_KP_3: keyvalue = '3'; break;
|
||||
case SDLK_KP_4: keyvalue = '4'; break;
|
||||
case SDLK_KP_5: keyvalue = '5'; break;
|
||||
case SDLK_KP_6: keyvalue = '6'; break;
|
||||
case SDLK_KP_7: keyvalue = '7'; break;
|
||||
case SDLK_KP_8: keyvalue = '8'; break;
|
||||
case SDLK_KP_9: keyvalue = '9'; break;
|
||||
case SDLK_KP_0: keyvalue = '0'; break;
|
||||
case SDLK_KP_PERIOD: keyvalue = '.'; break;
|
||||
case SDLK_KP_COMMA: keyvalue = ','; break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (keyvalue)
|
||||
{
|
||||
case SDLK_KP_DIVIDE: keyvalue = '/'; break;
|
||||
case SDLK_KP_MULTIPLY: keyvalue = '*'; break;
|
||||
case SDLK_KP_MINUS: keyvalue = '-'; break;
|
||||
case SDLK_KP_PLUS: keyvalue = '+'; break;
|
||||
}
|
||||
}
|
||||
if (keyvalue >= 0x80) keyvalue = 0; // Sadly ASCII only...
|
||||
return keyvalue;
|
||||
}
|
||||
|
||||
static void PostMouseMove(int x, int y)
|
||||
{
|
||||
static int lastx = 0, lasty = 0;
|
||||
event_t ev = { 0,0,0,0,0,0,0 };
|
||||
|
||||
ev.x = x;
|
||||
ev.y = y;
|
||||
lastx = x;
|
||||
lasty = y;
|
||||
if (ev.x | ev.y)
|
||||
{
|
||||
ev.type = EV_Mouse;
|
||||
D_PostEvent(&ev);
|
||||
}
|
||||
}
|
||||
|
||||
CVAR(Bool, m_noprescale, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
||||
static void MouseRead()
|
||||
{
|
||||
int x, y;
|
||||
|
||||
#if 0
|
||||
if (NativeMouse)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
SDL_GetRelativeMouseState(&x, &y);
|
||||
if (!m_noprescale)
|
||||
{
|
||||
x *= 3;
|
||||
y *= 2;
|
||||
}
|
||||
if (x | y)
|
||||
{
|
||||
PostMouseMove(x, -y);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t handleevents_pollsdl(void)
|
||||
{
|
||||
int32_t code, rv = 0, j;
|
||||
SDL_Event ev;
|
||||
event_t evt;
|
||||
|
||||
while (SDL_PollEvent(&ev))
|
||||
{
|
||||
switch (ev.type)
|
||||
{
|
||||
case SDL_TEXTINPUT:
|
||||
{
|
||||
j = 0;
|
||||
const uint8_t* text = (uint8_t*)ev.text.text;
|
||||
while ((j = GetCharFromString(text)))
|
||||
{
|
||||
code = ev.text.text[j];
|
||||
// Fixme: Send an EV_GUI_Event instead and properly deal with Unicode.
|
||||
if ((GUICapture & 1) && menuactive != MENU_WaitKey)
|
||||
{
|
||||
evt = { EV_GUI_Event, EV_GUI_Char, int16_t(j), !!(SDL_GetModState() & KMOD_ALT) };
|
||||
D_PostEvent(&evt);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
{
|
||||
if ((GUICapture & 1) && menuactive != MENU_WaitKey)
|
||||
{
|
||||
evt = {};
|
||||
evt.type = EV_GUI_Event;
|
||||
evt.subtype = ev.type == SDL_KEYDOWN ? EV_GUI_KeyDown : EV_GUI_KeyUp;
|
||||
SDL_Keymod kmod = SDL_GetModState();
|
||||
evt.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) |
|
||||
((kmod & KMOD_CTRL) ? GKM_CTRL : 0) |
|
||||
((kmod & KMOD_ALT) ? GKM_ALT : 0);
|
||||
|
||||
if (evt.subtype == EV_GUI_KeyDown && ev.key.repeat)
|
||||
{
|
||||
evt.subtype = EV_GUI_KeyRepeat;
|
||||
}
|
||||
|
||||
switch (ev.key.keysym.sym)
|
||||
{
|
||||
case SDLK_KP_ENTER: evt.data1 = GK_RETURN; break;
|
||||
case SDLK_PAGEUP: evt.data1 = GK_PGUP; break;
|
||||
case SDLK_PAGEDOWN: evt.data1 = GK_PGDN; break;
|
||||
case SDLK_END: evt.data1 = GK_END; break;
|
||||
case SDLK_HOME: evt.data1 = GK_HOME; break;
|
||||
case SDLK_LEFT: evt.data1 = GK_LEFT; break;
|
||||
case SDLK_RIGHT: evt.data1 = GK_RIGHT; break;
|
||||
case SDLK_UP: evt.data1 = GK_UP; break;
|
||||
case SDLK_DOWN: evt.data1 = GK_DOWN; break;
|
||||
case SDLK_DELETE: evt.data1 = GK_DEL; break;
|
||||
case SDLK_ESCAPE: evt.data1 = GK_ESCAPE; break;
|
||||
case SDLK_F1: evt.data1 = GK_F1; break;
|
||||
case SDLK_F2: evt.data1 = GK_F2; break;
|
||||
case SDLK_F3: evt.data1 = GK_F3; break;
|
||||
case SDLK_F4: evt.data1 = GK_F4; break;
|
||||
case SDLK_F5: evt.data1 = GK_F5; break;
|
||||
case SDLK_F6: evt.data1 = GK_F6; break;
|
||||
case SDLK_F7: evt.data1 = GK_F7; break;
|
||||
case SDLK_F8: evt.data1 = GK_F8; break;
|
||||
case SDLK_F9: evt.data1 = GK_F9; break;
|
||||
case SDLK_F10: evt.data1 = GK_F10; break;
|
||||
case SDLK_F11: evt.data1 = GK_F11; break;
|
||||
case SDLK_F12: evt.data1 = GK_F12; break;
|
||||
default:
|
||||
if (ev.key.keysym.sym < 256)
|
||||
{
|
||||
evt.data1 = ev.key.keysym.sym;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (evt.data1 < 128)
|
||||
{
|
||||
evt.data1 = toupper(evt.data1);
|
||||
D_PostEvent(&evt);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const& sc = ev.key.keysym.scancode;
|
||||
code = keytranslation[sc];
|
||||
|
||||
// The pause key generates a release event right after
|
||||
// the pressing one. As a result, it gets unseen
|
||||
// by the game most of the time.
|
||||
if (code == 0x59 && ev.type == SDL_KEYUP) // pause
|
||||
break;
|
||||
|
||||
int keyvalue = ev.type == SDL_KEYDOWN ? scancodetoasciihack(ev) : 0;
|
||||
event_t evt = { (uint8_t)(ev.type == SDL_KEYUP ? EV_KeyUp : EV_KeyDown), 0, (int16_t)code, (int16_t)keyvalue };
|
||||
D_PostEvent(&evt);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_MOUSEWHEEL:
|
||||
if (GUICapture)
|
||||
{
|
||||
evt.type = EV_GUI_Event;
|
||||
|
||||
if (ev.wheel.y == 0)
|
||||
evt.subtype = ev.wheel.x > 0 ? EV_GUI_WheelRight : EV_GUI_WheelLeft;
|
||||
else
|
||||
evt.subtype = ev.wheel.y > 0 ? EV_GUI_WheelUp : EV_GUI_WheelDown;
|
||||
|
||||
SDL_Keymod kmod = SDL_GetModState();
|
||||
evt.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) |
|
||||
((kmod & KMOD_CTRL) ? GKM_CTRL : 0) |
|
||||
((kmod & KMOD_ALT) ? GKM_ALT : 0);
|
||||
|
||||
D_PostEvent(&evt);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This never sends keyup events. They must be delayed for this to work with game buttons.
|
||||
if (ev.wheel.y > 0)
|
||||
{
|
||||
evt = { EV_KeyDown, 0, (int16_t)KEY_MWHEELUP };
|
||||
D_PostEvent(&evt);
|
||||
}
|
||||
if (ev.wheel.y < 0)
|
||||
{
|
||||
evt = { EV_KeyDown, 0, (int16_t)KEY_MWHEELDOWN };
|
||||
D_PostEvent(&evt);
|
||||
}
|
||||
if (ev.wheel.x > 0)
|
||||
{
|
||||
evt = { EV_KeyDown, 0, (int16_t)KEY_MWHEELRIGHT };
|
||||
D_PostEvent(&evt);
|
||||
}
|
||||
if (ev.wheel.y < 0)
|
||||
{
|
||||
evt = { EV_KeyDown, 0, (int16_t)KEY_MWHEELLEFT };
|
||||
D_PostEvent(&evt);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (ev.window.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
appactive = (ev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED);
|
||||
if (g_mouseGrabbed && g_mouseEnabled)
|
||||
grabmouse_low(appactive);
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_MOVED:
|
||||
{
|
||||
WindowMoved(ev.window.data1, ev.window.data2);
|
||||
break;
|
||||
}
|
||||
case SDL_WINDOWEVENT_ENTER:
|
||||
g_mouseInsideWindow = 1;
|
||||
break;
|
||||
case SDL_WINDOWEVENT_LEAVE:
|
||||
g_mouseInsideWindow = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SDL_MOUSEMOTION:
|
||||
//case SDL_JOYBALLMOTION:
|
||||
{
|
||||
// The menus need this, even in non GUI-capture mode
|
||||
event_t event;
|
||||
event.data1 = ev.motion.x;
|
||||
event.data2 = ev.motion.y;
|
||||
|
||||
//screen->ScaleCoordsFromWindow(event.data1, event.data2);
|
||||
|
||||
event.type = EV_GUI_Event;
|
||||
event.subtype = EV_GUI_MouseMove;
|
||||
|
||||
SDL_Keymod kmod = SDL_GetModState();
|
||||
event.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) |
|
||||
((kmod & KMOD_CTRL) ? GKM_CTRL : 0) |
|
||||
((kmod & KMOD_ALT) ? GKM_ALT : 0);
|
||||
|
||||
D_PostEvent(&event);
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
|
||||
if (!GUICapture)
|
||||
{
|
||||
evt.type = ev.type == SDL_MOUSEBUTTONDOWN ? EV_KeyDown : EV_KeyUp;
|
||||
|
||||
switch (ev.button.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT: evt.data1 = KEY_MOUSE1; break;
|
||||
case SDL_BUTTON_MIDDLE: evt.data1 = KEY_MOUSE3; break;
|
||||
case SDL_BUTTON_RIGHT: evt.data1 = KEY_MOUSE2; break;
|
||||
case SDL_BUTTON_X1: evt.data1 = KEY_MOUSE4; break;
|
||||
case SDL_BUTTON_X2: evt.data1 = KEY_MOUSE5; break;
|
||||
case 6: evt.data1 = KEY_MOUSE6; break;
|
||||
case 7: evt.data1 = KEY_MOUSE7; break;
|
||||
case 8: evt.data1 = KEY_MOUSE8; break;
|
||||
//default: printf("SDL mouse button %s %d\n", sev.type == SDL_MOUSEBUTTONDOWN ? "down" : "up", sev.button.button); break;
|
||||
}
|
||||
|
||||
if (evt.data1 != 0)
|
||||
{
|
||||
D_PostEvent(&evt);
|
||||
}
|
||||
}
|
||||
else if ((ev.button.button >= SDL_BUTTON_LEFT && ev.button.button <= SDL_BUTTON_X2))
|
||||
{
|
||||
int x, y;
|
||||
SDL_GetMouseState(&x, &y);
|
||||
|
||||
evt.type = EV_GUI_Event;
|
||||
evt.data1 = x;
|
||||
evt.data2 = y;
|
||||
|
||||
//screen->ScaleCoordsFromWindow(event.data1, event.data2);
|
||||
|
||||
if (ev.type == SDL_MOUSEBUTTONDOWN)
|
||||
{
|
||||
switch (ev.button.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT: evt.subtype = EV_GUI_LButtonDown; break;
|
||||
case SDL_BUTTON_MIDDLE: evt.subtype = EV_GUI_MButtonDown; break;
|
||||
case SDL_BUTTON_RIGHT: evt.subtype = EV_GUI_RButtonDown; break;
|
||||
case SDL_BUTTON_X1: evt.subtype = EV_GUI_BackButtonDown; break;
|
||||
case SDL_BUTTON_X2: evt.subtype = EV_GUI_FwdButtonDown; break;
|
||||
default: assert(false); evt.subtype = EV_GUI_None; break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (ev.button.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT: evt.subtype = EV_GUI_LButtonUp; break;
|
||||
case SDL_BUTTON_MIDDLE: evt.subtype = EV_GUI_MButtonUp; break;
|
||||
case SDL_BUTTON_RIGHT: evt.subtype = EV_GUI_RButtonUp; break;
|
||||
case SDL_BUTTON_X1: evt.subtype = EV_GUI_BackButtonUp; break;
|
||||
case SDL_BUTTON_X2: evt.subtype = EV_GUI_FwdButtonUp; break;
|
||||
default: assert(false); evt.subtype = EV_GUI_None; break;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Keymod kmod = SDL_GetModState();
|
||||
evt.data3 = ((kmod & KMOD_SHIFT) ? GKM_SHIFT : 0) |
|
||||
((kmod & KMOD_CTRL) ? GKM_CTRL : 0) |
|
||||
((kmod & KMOD_ALT) ? GKM_ALT : 0);
|
||||
|
||||
D_PostEvent(&evt);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_JOYHATMOTION:
|
||||
{
|
||||
int32_t hatvals[16] = {
|
||||
-1, // centre
|
||||
0, // up 1
|
||||
9000, // right 2
|
||||
4500, // up+right 3
|
||||
18000, // down 4
|
||||
-1, // down+up!! 5
|
||||
13500, // down+right 6
|
||||
-1, // down+right+up!! 7
|
||||
27000, // left 8
|
||||
27500, // left+up 9
|
||||
-1, // left+right!! 10
|
||||
-1, // left+right+up!! 11
|
||||
22500, // left+down 12
|
||||
-1, // left+down+up!! 13
|
||||
-1, // left+down+right!! 14
|
||||
-1, // left+down+right+up!! 15
|
||||
};
|
||||
if (appactive && ev.jhat.hat < joystick.numHats)
|
||||
joystick.pHat[ev.jhat.hat] = hatvals[ev.jhat.value & 15];
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_JOYBUTTONDOWN:
|
||||
case SDL_JOYBUTTONUP:
|
||||
#if 0 // SDL_MAJOR_VERSION >= 2
|
||||
if (joystick.isGameController)
|
||||
break;
|
||||
fallthrough__;
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
#endif
|
||||
if (!GUICapture)
|
||||
{
|
||||
evt.type = ev.type == SDL_JOYBUTTONDOWN ? EV_KeyDown : EV_KeyUp;
|
||||
evt.data1 = KEY_FIRSTJOYBUTTON + ev.jbutton.button;
|
||||
if (evt.data1 != 0)
|
||||
D_PostEvent(&evt);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_QUIT:
|
||||
throw ExitEvent(0); // completely bypass the hackery in the games to block Alt-F4.
|
||||
return -1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
MouseRead();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int32_t handleevents(void)
|
||||
{
|
||||
int32_t rv;
|
||||
|
||||
if (inputchecked && g_mouseEnabled)
|
||||
{
|
||||
// This is a horrible crutch
|
||||
if (inputState.mouseReadButtons() & WHEELUP_MOUSE)
|
||||
{
|
||||
event_t ev = { EV_KeyUp, 0, (int16_t)KEY_MWHEELUP };
|
||||
D_PostEvent(&ev);
|
||||
}
|
||||
if (inputState.mouseReadButtons() & WHEELDOWN_MOUSE)
|
||||
{
|
||||
event_t ev = { EV_KeyUp, 0, (int16_t)KEY_MWHEELDOWN };
|
||||
D_PostEvent(&ev);
|
||||
}
|
||||
if (inputState.mouseReadButtons() & WHEELLEFT_MOUSE)
|
||||
{
|
||||
event_t ev = { EV_KeyUp, 0, (int16_t)KEY_MWHEELLEFT };
|
||||
D_PostEvent(&ev);
|
||||
}
|
||||
if (inputState.mouseReadButtons() & WHEELRIGHT_MOUSE)
|
||||
{
|
||||
event_t ev = { EV_KeyUp, 0, (int16_t)KEY_MWHEELRIGHT };
|
||||
D_PostEvent(&ev);
|
||||
}
|
||||
}
|
||||
|
||||
rv = handleevents_pollsdl();
|
||||
|
||||
inputchecked = 0;
|
||||
timerUpdateClock();
|
||||
//I_ProcessJoysticks();
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
int32_t handleevents_peekkeys(void)
|
||||
{
|
||||
SDL_PumpEvents();
|
||||
|
||||
return SDL_PeepEvents(NULL, 1, SDL_PEEKEVENT, SDL_KEYDOWN, SDL_KEYDOWN);
|
||||
}
|
||||
|
||||
void I_SetMouseCapture()
|
||||
{
|
||||
// Clear out any mouse movement.
|
||||
SDL_CaptureMouse(SDL_TRUE);
|
||||
}
|
||||
|
||||
void I_ReleaseMouseCapture()
|
||||
{
|
||||
SDL_CaptureMouse(SDL_FALSE);
|
||||
}
|
Loading…
Reference in a new issue