mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-25 11:10:47 +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()
|
void I_ProcessJoysticks()
|
||||||
{
|
{
|
||||||
if (use_joystick && JoystickManager)
|
if (use_joystick)
|
||||||
JoystickManager->ProcessInput();
|
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