mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-11-14 08:30:50 +00:00
Merge branch 'master' of https://github.com/rheit/zdoom
This commit is contained in:
commit
87e9ba0f05
16 changed files with 857 additions and 582 deletions
|
@ -190,6 +190,7 @@ else()
|
||||||
set( FMOD_INC_PATH_SUFFIXES PATH_SUFFIXES inc )
|
set( FMOD_INC_PATH_SUFFIXES PATH_SUFFIXES inc )
|
||||||
set( FMOD_LIB_PATH_SUFFIXES PATH_SUFFIXES lib )
|
set( FMOD_LIB_PATH_SUFFIXES PATH_SUFFIXES lib )
|
||||||
set( NO_GTK ON )
|
set( NO_GTK ON )
|
||||||
|
set( DYN_GTK OFF )
|
||||||
|
|
||||||
# Prevent inclusion of fp.h and FixMath.h from Carbon framework
|
# Prevent inclusion of fp.h and FixMath.h from Carbon framework
|
||||||
# Declarations from these files are not used but cause the following conflicts:
|
# Declarations from these files are not used but cause the following conflicts:
|
||||||
|
@ -198,6 +199,7 @@ else()
|
||||||
add_definitions( -D__FP__ -D__FIXMATH__ )
|
add_definitions( -D__FP__ -D__FIXMATH__ )
|
||||||
else()
|
else()
|
||||||
option( NO_GTK "Disable GTK+ dialogs (Not applicable to Windows)" )
|
option( NO_GTK "Disable GTK+ dialogs (Not applicable to Windows)" )
|
||||||
|
option( DYN_GTK "Load GTK+ at runtime instead of compile time" ON )
|
||||||
option( VALGRIND "Add special Valgrind sequences to self-modifying code" )
|
option( VALGRIND "Add special Valgrind sequences to self-modifying code" )
|
||||||
|
|
||||||
set( FMOD_SEARCH_PATHS
|
set( FMOD_SEARCH_PATHS
|
||||||
|
@ -213,9 +215,19 @@ else()
|
||||||
|
|
||||||
# Use GTK+ for the IWAD picker, if available.
|
# Use GTK+ for the IWAD picker, if available.
|
||||||
if( NOT NO_GTK )
|
if( NOT NO_GTK )
|
||||||
|
pkg_check_modules( GTK3 gtk+-3.0 )
|
||||||
|
if( GTK3_FOUND )
|
||||||
|
if( NOT DYN_GTK )
|
||||||
|
set( ZDOOM_LIBS ${ZDOOM_LIBS} ${GTK3_LIBRARIES} )
|
||||||
|
endif()
|
||||||
|
include_directories( ${GTK3_INCLUDE_DIRS} )
|
||||||
|
link_directories( ${GTK3_LIBRARY_DIRS} )
|
||||||
|
else()
|
||||||
pkg_check_modules( GTK2 gtk+-2.0 )
|
pkg_check_modules( GTK2 gtk+-2.0 )
|
||||||
if( GTK2_FOUND )
|
if( GTK2_FOUND )
|
||||||
|
if( NOT DYN_GTK )
|
||||||
set( ZDOOM_LIBS ${ZDOOM_LIBS} ${GTK2_LIBRARIES} )
|
set( ZDOOM_LIBS ${ZDOOM_LIBS} ${GTK2_LIBRARIES} )
|
||||||
|
endif()
|
||||||
include_directories( ${GTK2_INCLUDE_DIRS} )
|
include_directories( ${GTK2_INCLUDE_DIRS} )
|
||||||
link_directories( ${GTK2_LIBRARY_DIRS} )
|
link_directories( ${GTK2_LIBRARY_DIRS} )
|
||||||
else()
|
else()
|
||||||
|
@ -223,10 +235,15 @@ else()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
endif()
|
||||||
set( NASM_NAMES nasm )
|
set( NASM_NAMES nasm )
|
||||||
|
|
||||||
if( NO_GTK )
|
if( NO_GTK )
|
||||||
add_definitions( -DNO_GTK=1 )
|
add_definitions( -DNO_GTK )
|
||||||
|
elseif( DYN_GTK )
|
||||||
|
add_definitions( -DDYN_GTK=1 )
|
||||||
|
else()
|
||||||
|
add_definitions( -DDYN_GTK=0 )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Non-Windows version also needs SDL except native OS X backend
|
# Non-Windows version also needs SDL except native OS X backend
|
||||||
|
@ -761,7 +778,8 @@ set( PLAT_SDL_SOURCES
|
||||||
posix/sdl/sdlglvideo.cpp
|
posix/sdl/sdlglvideo.cpp
|
||||||
posix/sdl/st_start.cpp )
|
posix/sdl/st_start.cpp )
|
||||||
set( PLAT_UNIX_SOURCES
|
set( PLAT_UNIX_SOURCES
|
||||||
posix/unix/i_specialpaths.cpp )
|
posix/unix/i_specialpaths.cpp
|
||||||
|
posix/unix/iwadpicker_gtk.cpp )
|
||||||
set( PLAT_OSX_SOURCES
|
set( PLAT_OSX_SOURCES
|
||||||
posix/osx/iwadpicker_cocoa.mm
|
posix/osx/iwadpicker_cocoa.mm
|
||||||
posix/osx/i_specialpaths.mm
|
posix/osx/i_specialpaths.mm
|
||||||
|
@ -1282,6 +1300,7 @@ set (PCH_SOURCES
|
||||||
gi.cpp
|
gi.cpp
|
||||||
gitinfo.cpp
|
gitinfo.cpp
|
||||||
hu_scores.cpp
|
hu_scores.cpp
|
||||||
|
i_module.cpp
|
||||||
i_net.cpp
|
i_net.cpp
|
||||||
info.cpp
|
info.cpp
|
||||||
keysections.cpp
|
keysections.cpp
|
||||||
|
|
101
src/i_module.cpp
Normal file
101
src/i_module.cpp
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
** i_module.cpp
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 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.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "i_module.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#define USE_WINDOWS_DWORD
|
||||||
|
#else
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#define LoadLibrary(x) dlopen((x), RTLD_LAZY)
|
||||||
|
#define GetProcAddress(a,b) dlsym((a),(b))
|
||||||
|
#define FreeLibrary(x) dlclose((x))
|
||||||
|
using HMODULE = void*;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool FModule::Load(std::initializer_list<const char*> libnames)
|
||||||
|
{
|
||||||
|
for(auto lib : libnames)
|
||||||
|
{
|
||||||
|
if(!Open(lib))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
StaticProc *proc;
|
||||||
|
for(proc = reqSymbols;proc;proc = proc->Next)
|
||||||
|
{
|
||||||
|
if(!(proc->Call = GetSym(proc->Name)) && !proc->Optional)
|
||||||
|
{
|
||||||
|
Unload();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(IsLoaded())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FModule::Unload()
|
||||||
|
{
|
||||||
|
if(handle)
|
||||||
|
{
|
||||||
|
FreeLibrary((HMODULE)handle);
|
||||||
|
handle = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FModule::Open(const char* lib)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
if((handle = GetModuleHandle(lib)) != nullptr)
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
// Loading an empty string in Linux doesn't do what we expect it to.
|
||||||
|
if(*lib == '\0')
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
handle = LoadLibrary(lib);
|
||||||
|
return handle != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *FModule::GetSym(const char* name)
|
||||||
|
{
|
||||||
|
return GetProcAddress((HMODULE)handle, name);
|
||||||
|
}
|
229
src/i_module.h
Normal file
229
src/i_module.h
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
/*
|
||||||
|
** i_module.h
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 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.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
|
/* FModule Run Time Library Loader
|
||||||
|
*
|
||||||
|
* This provides an interface for loading optional dependencies or detecting
|
||||||
|
* version specific symbols at run time. These classes largely provide an
|
||||||
|
* interface for statically declaring the symbols that are going to be used
|
||||||
|
* ahead of time, thus should not be used on the stack or as part of a
|
||||||
|
* dynamically allocated object. The procedure templates take the FModule
|
||||||
|
* as a template argument largely to make such use of FModule awkward.
|
||||||
|
*
|
||||||
|
* Declared procedures register themselves with FModule and the module will not
|
||||||
|
* be considered loaded unless all required procedures can be resolved. In
|
||||||
|
* order to remove the need for boilerplate code, optional procedures do not
|
||||||
|
* enforce the requirement that the value is null checked before use. As a
|
||||||
|
* debugging aid debug builds will check that operator bool was called at some
|
||||||
|
* point, but this is just a first order sanity check.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class FModule;
|
||||||
|
class FStaticModule;
|
||||||
|
|
||||||
|
template<FModule &Module, typename Proto>
|
||||||
|
class TOptProc;
|
||||||
|
|
||||||
|
template<FModule &Module, typename Proto>
|
||||||
|
class TReqProc;
|
||||||
|
|
||||||
|
template<FStaticModule &Module, typename Proto, Proto Sym>
|
||||||
|
class TStaticProc;
|
||||||
|
|
||||||
|
class FModule
|
||||||
|
{
|
||||||
|
template<FModule &Module, typename Proto>
|
||||||
|
friend class TOptProc;
|
||||||
|
template<FModule &Module, typename Proto>
|
||||||
|
friend class TReqProc;
|
||||||
|
|
||||||
|
struct StaticProc
|
||||||
|
{
|
||||||
|
void *Call;
|
||||||
|
const char* Name;
|
||||||
|
StaticProc *Next;
|
||||||
|
bool Optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
void RegisterStatic(StaticProc &proc)
|
||||||
|
{
|
||||||
|
proc.Next = reqSymbols;
|
||||||
|
reqSymbols = &proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *handle = nullptr;
|
||||||
|
|
||||||
|
// Debugging aid
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
// Since FModule is supposed to be statically allocated it is assumed that
|
||||||
|
// reqSymbols will be initialized to nullptr avoiding initialization order
|
||||||
|
// problems with declaring procedures.
|
||||||
|
StaticProc *reqSymbols;
|
||||||
|
|
||||||
|
bool Open(const char* lib);
|
||||||
|
void *GetSym(const char* name);
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<FModule &Module, typename Proto, Proto Sym>
|
||||||
|
using Opt = TOptProc<Module, Proto>;
|
||||||
|
template<FModule &Module, typename Proto, Proto Sym>
|
||||||
|
using Req = TReqProc<Module, Proto>;
|
||||||
|
|
||||||
|
FModule(const char* name) : name(name) {};
|
||||||
|
~FModule() { Unload(); }
|
||||||
|
|
||||||
|
// Load a shared library using the first library name which satisfies all
|
||||||
|
// of the required symbols.
|
||||||
|
bool Load(std::initializer_list<const char*> libnames);
|
||||||
|
void Unload();
|
||||||
|
|
||||||
|
bool IsLoaded() const { return handle != nullptr; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Null version of FModule which satisfies the API so the same code can be used
|
||||||
|
// for run time and compile time linking.
|
||||||
|
class FStaticModule
|
||||||
|
{
|
||||||
|
template<FStaticModule &Module, typename Proto, Proto Sym>
|
||||||
|
friend class TStaticProc;
|
||||||
|
|
||||||
|
const char *name;
|
||||||
|
public:
|
||||||
|
template<FStaticModule &Module, typename Proto, Proto Sym>
|
||||||
|
using Opt = TStaticProc<Module, Proto, Sym>;
|
||||||
|
template<FStaticModule &Module, typename Proto, Proto Sym>
|
||||||
|
using Req = TStaticProc<Module, Proto, Sym>;
|
||||||
|
|
||||||
|
FStaticModule(const char* name) : name(name) {};
|
||||||
|
|
||||||
|
bool Load(std::initializer_list<const char*> libnames) { return true; }
|
||||||
|
void Unload() {}
|
||||||
|
|
||||||
|
bool IsLoaded() const { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allow FModuleMaybe<DYN_XYZ> to switch based on preprocessor flag.
|
||||||
|
// Use FModuleMaybe<DYN_XYZ>::Opt and FModuleMaybe<DYN_XYZ>::Req for procs.
|
||||||
|
template<bool Dynamic>
|
||||||
|
struct TModuleType { using Type = FModule; };
|
||||||
|
template<>
|
||||||
|
struct TModuleType<false> { using Type = FStaticModule; };
|
||||||
|
|
||||||
|
template<bool Dynamic>
|
||||||
|
using FModuleMaybe = typename TModuleType<Dynamic>::Type;
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<FModule &Module, typename Proto>
|
||||||
|
class TOptProc
|
||||||
|
{
|
||||||
|
FModule::StaticProc proc;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
mutable bool checked = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// I am not a pointer
|
||||||
|
bool operator==(void*) const;
|
||||||
|
bool operator!=(void*) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TOptProc(const char* function)
|
||||||
|
{
|
||||||
|
proc.Name = function;
|
||||||
|
proc.Optional = true;
|
||||||
|
Module.RegisterStatic(proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
operator Proto() const
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
assert(checked);
|
||||||
|
#endif
|
||||||
|
return (Proto)proc.Call;
|
||||||
|
}
|
||||||
|
explicit operator bool() const
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
assert(Module.IsLoaded());
|
||||||
|
checked = true;
|
||||||
|
#endif
|
||||||
|
return proc.Call != nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<FModule &Module, typename Proto>
|
||||||
|
class TReqProc
|
||||||
|
{
|
||||||
|
FModule::StaticProc proc;
|
||||||
|
|
||||||
|
// I am not a pointer
|
||||||
|
bool operator==(void*) const;
|
||||||
|
bool operator!=(void*) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TReqProc(const char* function)
|
||||||
|
{
|
||||||
|
proc.Name = function;
|
||||||
|
proc.Optional = false;
|
||||||
|
Module.RegisterStatic(proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
operator Proto() const
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
assert(Module.IsLoaded());
|
||||||
|
#endif
|
||||||
|
return (Proto)proc.Call;
|
||||||
|
}
|
||||||
|
explicit operator bool() const { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<FStaticModule &Module, typename Proto, Proto Sym>
|
||||||
|
class TStaticProc
|
||||||
|
{
|
||||||
|
// I am not a pointer
|
||||||
|
bool operator==(void*) const;
|
||||||
|
bool operator!=(void*) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TStaticProc(const char* function) {}
|
||||||
|
|
||||||
|
operator Proto() const { return Sym; }
|
||||||
|
explicit operator bool() const { return Sym != nullptr; }
|
||||||
|
};
|
|
@ -40,9 +40,6 @@
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#ifndef NO_GTK
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#endif
|
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#if defined(__MACH__) && !defined(NOASM)
|
#if defined(__MACH__) && !defined(NOASM)
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
@ -87,10 +84,6 @@ void Mac_I_FatalError(const char* errortext);
|
||||||
|
|
||||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||||
|
|
||||||
#ifndef NO_GTK
|
|
||||||
bool GtkAvailable;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// The command line arguments.
|
// The command line arguments.
|
||||||
DArgs *Args;
|
DArgs *Args;
|
||||||
|
|
||||||
|
@ -259,10 +252,6 @@ int main (int argc, char **argv)
|
||||||
// Note that the LANG environment variable is overridden by LC_*
|
// Note that the LANG environment variable is overridden by LC_*
|
||||||
setenv ("LC_NUMERIC", "C", 1);
|
setenv ("LC_NUMERIC", "C", 1);
|
||||||
|
|
||||||
#ifndef NO_GTK
|
|
||||||
GtkAvailable = gtk_init_check (&argc, &argv);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
setlocale (LC_ALL, "C");
|
setlocale (LC_ALL, "C");
|
||||||
|
|
||||||
if (SDL_Init (0) < 0)
|
if (SDL_Init (0) < 0)
|
||||||
|
|
|
@ -34,10 +34,8 @@
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#ifndef NO_GTK
|
|
||||||
#include <gtk/gtk.h>
|
#include <SDL.h>
|
||||||
#include <gdk/gdkkeysyms.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "doomerrors.h"
|
#include "doomerrors.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
@ -71,10 +69,6 @@
|
||||||
#include "m_fixed.h"
|
#include "m_fixed.h"
|
||||||
#include "g_level.h"
|
#include "g_level.h"
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#include <ApplicationServices/ApplicationServices.h>
|
|
||||||
#endif // __APPLE__
|
|
||||||
|
|
||||||
EXTERN_CVAR (String, language)
|
EXTERN_CVAR (String, language)
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
|
@ -84,7 +78,8 @@ extern "C"
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NO_GTK
|
#ifndef NO_GTK
|
||||||
extern bool GtkAvailable;
|
bool I_GtkAvailable ();
|
||||||
|
int I_PickIWad_Gtk (WadStuff *wads, int numwads, bool showwin, int defaultiwad);
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad);
|
int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad);
|
||||||
#endif
|
#endif
|
||||||
|
@ -262,183 +257,6 @@ void I_PrintStr (const char *cp)
|
||||||
fflush (stdout);
|
fflush (stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NO_GTK
|
|
||||||
// GtkTreeViews eats return keys. I want this to be like a Windows listbox
|
|
||||||
// where pressing Return can still activate the default button.
|
|
||||||
gint AllowDefault(GtkWidget *widget, GdkEventKey *event, gpointer func_data)
|
|
||||||
{
|
|
||||||
if (event->type == GDK_KEY_PRESS && event->keyval == GDK_Return)
|
|
||||||
{
|
|
||||||
gtk_window_activate_default (GTK_WINDOW(func_data));
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Double-clicking an entry in the list is the same as pressing OK.
|
|
||||||
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.
|
|
||||||
gint CheckEscape (GtkWidget *widget, GdkEventKey *event, gpointer func_data)
|
|
||||||
{
|
|
||||||
if (event->type == GDK_KEY_PRESS && event->keyval == GDK_Escape)
|
|
||||||
{
|
|
||||||
gtk_main_quit();
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClickedOK(GtkButton *button, gpointer func_data)
|
|
||||||
{
|
|
||||||
*(int *)func_data = 1;
|
|
||||||
gtk_main_quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
EXTERN_CVAR (Bool, queryiwad);
|
|
||||||
|
|
||||||
int I_PickIWad_Gtk (WadStuff *wads, int numwads, bool showwin, int defaultiwad)
|
|
||||||
{
|
|
||||||
GtkWidget *window;
|
|
||||||
GtkWidget *vbox;
|
|
||||||
GtkWidget *hbox;
|
|
||||||
GtkWidget *bbox;
|
|
||||||
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_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.
|
|
||||||
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);
|
|
||||||
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.
|
|
||||||
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.
|
|
||||||
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_from_stock (GTK_STOCK_OK);
|
|
||||||
gtk_box_pack_start (GTK_BOX(bbox), widget, false, false, 0);
|
|
||||||
GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_DEFAULT);
|
|
||||||
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_from_stock (GTK_STOCK_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;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad)
|
int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -448,7 +266,7 @@ int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad)
|
||||||
return defaultiwad;
|
return defaultiwad;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(__APPLE__)
|
#ifndef __APPLE__
|
||||||
const char *str;
|
const char *str;
|
||||||
if((str=getenv("KDE_FULL_SESSION")) && strcmp(str, "true") == 0)
|
if((str=getenv("KDE_FULL_SESSION")) && strcmp(str, "true") == 0)
|
||||||
{
|
{
|
||||||
|
@ -500,12 +318,15 @@ int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef NO_GTK
|
#ifndef NO_GTK
|
||||||
if (GtkAvailable)
|
if (I_GtkAvailable())
|
||||||
{
|
{
|
||||||
return I_PickIWad_Gtk (wads, numwads, showwin, defaultiwad);
|
return I_PickIWad_Gtk (wads, numwads, showwin, defaultiwad);
|
||||||
}
|
}
|
||||||
#elif defined(__APPLE__)
|
#endif
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
return I_PickIWad_Cocoa (wads, numwads, showwin, defaultiwad);
|
return I_PickIWad_Cocoa (wads, numwads, showwin, defaultiwad);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -605,139 +426,19 @@ int I_FindAttr (findstate_t *fileinfo)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
static PasteboardRef s_clipboard;
|
|
||||||
|
|
||||||
static CFDataRef GetPasteboardData(const PasteboardItemID itemID, const CFStringRef flavorType)
|
|
||||||
{
|
|
||||||
CFDataRef data = NULL;
|
|
||||||
|
|
||||||
const OSStatus result = PasteboardCopyItemFlavorData(s_clipboard, itemID, flavorType, &data);
|
|
||||||
|
|
||||||
return noErr == result ? data : NULL;
|
|
||||||
}
|
|
||||||
#endif // __APPLE__
|
|
||||||
|
|
||||||
// Clipboard support requires GTK+
|
|
||||||
// TODO: GTK+ uses UTF-8. We don't, so some conversions would be appropriate.
|
|
||||||
void I_PutInClipboard (const char *str)
|
void I_PutInClipboard (const char *str)
|
||||||
{
|
{
|
||||||
#ifndef NO_GTK
|
SDL_SetClipboardText(str);
|
||||||
if (GtkAvailable)
|
|
||||||
{
|
|
||||||
GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
|
|
||||||
if (clipboard != NULL)
|
|
||||||
{
|
|
||||||
gtk_clipboard_set_text(clipboard, str, -1);
|
|
||||||
}
|
|
||||||
/* Should I? I don't know. It's not actually a selection.
|
|
||||||
clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
|
|
||||||
if (clipboard != NULL)
|
|
||||||
{
|
|
||||||
gtk_clipboard_set_text(clipboard, str, -1);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
#elif defined __APPLE__
|
|
||||||
if (NULL == s_clipboard)
|
|
||||||
{
|
|
||||||
PasteboardCreate(kPasteboardClipboard, &s_clipboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
PasteboardClear(s_clipboard);
|
|
||||||
PasteboardSynchronize(s_clipboard);
|
|
||||||
|
|
||||||
const CFDataRef textData = CFDataCreate(kCFAllocatorDefault,
|
|
||||||
reinterpret_cast<const UInt8*>(str), strlen(str));
|
|
||||||
|
|
||||||
if (NULL != textData)
|
|
||||||
{
|
|
||||||
PasteboardPutItemFlavor(s_clipboard, PasteboardItemID(1),
|
|
||||||
CFSTR("public.utf8-plain-text"), textData, 0);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FString I_GetFromClipboard (bool use_primary_selection)
|
FString I_GetFromClipboard (bool use_primary_selection)
|
||||||
{
|
{
|
||||||
#ifndef NO_GTK
|
if(char *ret = SDL_GetClipboardText())
|
||||||
if (GtkAvailable)
|
|
||||||
{
|
{
|
||||||
GtkClipboard *clipboard = gtk_clipboard_get(use_primary_selection ?
|
FString text(ret);
|
||||||
GDK_SELECTION_PRIMARY : GDK_SELECTION_CLIPBOARD);
|
SDL_free(ret);
|
||||||
if (clipboard != NULL)
|
return text;
|
||||||
{
|
|
||||||
gchar *text = gtk_clipboard_wait_for_text(clipboard);
|
|
||||||
if (text != NULL)
|
|
||||||
{
|
|
||||||
FString copy(text);
|
|
||||||
g_free(text);
|
|
||||||
return copy;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
#elif defined __APPLE__
|
|
||||||
FString result;
|
|
||||||
|
|
||||||
if (NULL == s_clipboard)
|
|
||||||
{
|
|
||||||
PasteboardCreate(kPasteboardClipboard, &s_clipboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
PasteboardSynchronize(s_clipboard);
|
|
||||||
|
|
||||||
ItemCount itemCount = 0;
|
|
||||||
PasteboardGetItemCount(s_clipboard, &itemCount);
|
|
||||||
|
|
||||||
if (0 == itemCount)
|
|
||||||
{
|
|
||||||
return FString();
|
|
||||||
}
|
|
||||||
|
|
||||||
PasteboardItemID itemID;
|
|
||||||
|
|
||||||
if (0 != PasteboardGetItemIdentifier(s_clipboard, 1, &itemID))
|
|
||||||
{
|
|
||||||
return FString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CFDataRef data = GetPasteboardData(itemID, kUTTypeUTF8PlainText))
|
|
||||||
{
|
|
||||||
const CFIndex bufferLength = CFDataGetLength(data);
|
|
||||||
char* const buffer = result.LockNewBuffer(bufferLength);
|
|
||||||
|
|
||||||
memcpy(buffer, CFDataGetBytePtr(data), bufferLength);
|
|
||||||
|
|
||||||
result.UnlockBuffer();
|
|
||||||
}
|
|
||||||
else if (CFDataRef data = GetPasteboardData(itemID, kUTTypeUTF16PlainText))
|
|
||||||
{
|
|
||||||
#ifdef __LITTLE_ENDIAN__
|
|
||||||
static const CFStringEncoding ENCODING = kCFStringEncodingUTF16LE;
|
|
||||||
#else // __BIG_ENDIAN__
|
|
||||||
static const CFStringEncoding ENCODING = kCFStringEncodingUTF16BE;
|
|
||||||
#endif // __LITTLE_ENDIAN__
|
|
||||||
|
|
||||||
if (const CFStringRef utf16 = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, data, ENCODING))
|
|
||||||
{
|
|
||||||
const CFRange range = { 0, CFStringGetLength(utf16) };
|
|
||||||
CFIndex bufferLength = 0;
|
|
||||||
|
|
||||||
if (CFStringGetBytes(utf16, range, kCFStringEncodingUTF8, '?', false, NULL, 0, &bufferLength) > 0)
|
|
||||||
{
|
|
||||||
UInt8* const buffer = reinterpret_cast<UInt8*>(result.LockNewBuffer(bufferLength));
|
|
||||||
|
|
||||||
CFStringGetBytes(utf16, range, kCFStringEncodingUTF8, '?', false, buffer, bufferLength, NULL);
|
|
||||||
|
|
||||||
result.UnlockBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
CFRelease(utf16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
#endif
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
331
src/posix/unix/iwadpicker_gtk.cpp
Normal file
331
src/posix/unix/iwadpicker_gtk.cpp
Normal file
|
@ -0,0 +1,331 @@
|
||||||
|
#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>
|
||||||
|
#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);
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Gtk
|
||||||
|
|
||||||
|
int I_PickIWad_Gtk (WadStuff *wads, int numwads, bool showwin, int defaultiwad)
|
||||||
|
{
|
||||||
|
return Gtk::PickIWad (wads, numwads, showwin, defaultiwad);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
|
@ -359,6 +359,9 @@ protected:
|
||||||
#ifndef DYN_FLUIDSYNTH
|
#ifndef DYN_FLUIDSYNTH
|
||||||
#include <fluidsynth.h>
|
#include <fluidsynth.h>
|
||||||
#else
|
#else
|
||||||
|
#include "i_module.h"
|
||||||
|
extern FModule FluidSynthModule;
|
||||||
|
|
||||||
struct fluid_settings_t;
|
struct fluid_settings_t;
|
||||||
struct fluid_synth_t;
|
struct fluid_synth_t;
|
||||||
#endif
|
#endif
|
||||||
|
@ -386,40 +389,35 @@ protected:
|
||||||
|
|
||||||
#ifdef DYN_FLUIDSYNTH
|
#ifdef DYN_FLUIDSYNTH
|
||||||
enum { FLUID_FAILED = -1, FLUID_OK = 0 };
|
enum { FLUID_FAILED = -1, FLUID_OK = 0 };
|
||||||
fluid_settings_t *(*new_fluid_settings)();
|
static TReqProc<FluidSynthModule, fluid_settings_t *(*)()> new_fluid_settings;
|
||||||
fluid_synth_t *(*new_fluid_synth)(fluid_settings_t *);
|
static TReqProc<FluidSynthModule, fluid_synth_t *(*)(fluid_settings_t *)> new_fluid_synth;
|
||||||
int (*delete_fluid_synth)(fluid_synth_t *);
|
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *)> delete_fluid_synth;
|
||||||
void (*delete_fluid_settings)(fluid_settings_t *);
|
static TReqProc<FluidSynthModule, void (*)(fluid_settings_t *)> delete_fluid_settings;
|
||||||
int (*fluid_settings_setnum)(fluid_settings_t *, const char *, double);
|
static TReqProc<FluidSynthModule, int (*)(fluid_settings_t *, const char *, double)> fluid_settings_setnum;
|
||||||
int (*fluid_settings_setstr)(fluid_settings_t *, const char *, const char *);
|
static TReqProc<FluidSynthModule, int (*)(fluid_settings_t *, const char *, const char *)> fluid_settings_setstr;
|
||||||
int (*fluid_settings_setint)(fluid_settings_t *, const char *, int);
|
static TReqProc<FluidSynthModule, int (*)(fluid_settings_t *, const char *, int)> fluid_settings_setint;
|
||||||
int (*fluid_settings_getstr)(fluid_settings_t *, const char *, char **);
|
static TReqProc<FluidSynthModule, int (*)(fluid_settings_t *, const char *, char **)> fluid_settings_getstr;
|
||||||
int (*fluid_settings_getint)(fluid_settings_t *, const char *, int *);
|
static TReqProc<FluidSynthModule, int (*)(fluid_settings_t *, const char *, int *)> fluid_settings_getint;
|
||||||
void (*fluid_synth_set_reverb_on)(fluid_synth_t *, int);
|
static TReqProc<FluidSynthModule, void (*)(fluid_synth_t *, int)> fluid_synth_set_reverb_on;
|
||||||
void (*fluid_synth_set_chorus_on)(fluid_synth_t *, int);
|
static TReqProc<FluidSynthModule, void (*)(fluid_synth_t *, int)> fluid_synth_set_chorus_on;
|
||||||
int (*fluid_synth_set_interp_method)(fluid_synth_t *, int, int);
|
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int, int)> fluid_synth_set_interp_method;
|
||||||
int (*fluid_synth_set_polyphony)(fluid_synth_t *, int);
|
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int)> fluid_synth_set_polyphony;
|
||||||
int (*fluid_synth_get_polyphony)(fluid_synth_t *);
|
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *)> fluid_synth_get_polyphony;
|
||||||
int (*fluid_synth_get_active_voice_count)(fluid_synth_t *);
|
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *)> fluid_synth_get_active_voice_count;
|
||||||
double (*fluid_synth_get_cpu_load)(fluid_synth_t *);
|
static TReqProc<FluidSynthModule, double (*)(fluid_synth_t *)> fluid_synth_get_cpu_load;
|
||||||
int (*fluid_synth_system_reset)(fluid_synth_t *);
|
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *)> fluid_synth_system_reset;
|
||||||
int (*fluid_synth_noteon)(fluid_synth_t *, int, int, int);
|
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int, int, int)> fluid_synth_noteon;
|
||||||
int (*fluid_synth_noteoff)(fluid_synth_t *, int, int);
|
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int, int)> fluid_synth_noteoff;
|
||||||
int (*fluid_synth_cc)(fluid_synth_t *, int, int, int);
|
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int, int, int)> fluid_synth_cc;
|
||||||
int (*fluid_synth_program_change)(fluid_synth_t *, int, int);
|
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int, int)> fluid_synth_program_change;
|
||||||
int (*fluid_synth_channel_pressure)(fluid_synth_t *, int, int);
|
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int, int)> fluid_synth_channel_pressure;
|
||||||
int (*fluid_synth_pitch_bend)(fluid_synth_t *, int, int);
|
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int, int)> fluid_synth_pitch_bend;
|
||||||
int (*fluid_synth_write_float)(fluid_synth_t *, int, void *, int, int, void *, int, int);
|
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int, void *, int, int, void *, int, int)> fluid_synth_write_float;
|
||||||
int (*fluid_synth_sfload)(fluid_synth_t *, const char *, int);
|
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, const char *, int)> fluid_synth_sfload;
|
||||||
void (*fluid_synth_set_reverb)(fluid_synth_t *, double, double, double, double);
|
static TReqProc<FluidSynthModule, void (*)(fluid_synth_t *, double, double, double, double)> fluid_synth_set_reverb;
|
||||||
void (*fluid_synth_set_chorus)(fluid_synth_t *, int, double, double, double, int);
|
static TReqProc<FluidSynthModule, void (*)(fluid_synth_t *, int, double, double, double, int)> fluid_synth_set_chorus;
|
||||||
int (*fluid_synth_sysex)(fluid_synth_t *, const char *, int, char *, int *, int *, int);
|
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, const char *, int, char *, int *, int *, int)> fluid_synth_sysex;
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
HMODULE FluidSynthDLL;
|
|
||||||
#else
|
|
||||||
void *FluidSynthSO;
|
|
||||||
#endif
|
|
||||||
bool LoadFluidSynth();
|
bool LoadFluidSynth();
|
||||||
void UnloadFluidSynth();
|
void UnloadFluidSynth();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -60,9 +60,9 @@
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
#define FLUIDSYNTHLIB "libfluidsynth.1.dylib"
|
#define FLUIDSYNTHLIB1 "libfluidsynth.1.dylib"
|
||||||
#else // !__APPLE__
|
#else // !__APPLE__
|
||||||
#define FLUIDSYNTHLIB "libfluidsynth.so.1"
|
#define FLUIDSYNTHLIB1 "libfluidsynth.so.1"
|
||||||
#endif // __APPLE__
|
#endif // __APPLE__
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -644,12 +644,6 @@ FString FluidSynthMIDIDevice::GetStats()
|
||||||
|
|
||||||
#ifdef DYN_FLUIDSYNTH
|
#ifdef DYN_FLUIDSYNTH
|
||||||
|
|
||||||
struct LibFunc
|
|
||||||
{
|
|
||||||
void **FuncPointer;
|
|
||||||
const char *FuncName;
|
|
||||||
};
|
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// FluidSynthMIDIDevice :: LoadFluidSynth
|
// FluidSynthMIDIDevice :: LoadFluidSynth
|
||||||
|
@ -658,125 +652,66 @@ struct LibFunc
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
|
FModuleMaybe<DYN_FLUIDSYNTH> FluidSynthModule{"FluidSynth"};
|
||||||
|
|
||||||
|
#define DYN_FLUID_SYM(x) decltype(FluidSynthMIDIDevice::x) FluidSynthMIDIDevice::x{#x}
|
||||||
|
DYN_FLUID_SYM(new_fluid_settings);
|
||||||
|
DYN_FLUID_SYM(new_fluid_synth);
|
||||||
|
DYN_FLUID_SYM(delete_fluid_synth);
|
||||||
|
DYN_FLUID_SYM(delete_fluid_settings);
|
||||||
|
DYN_FLUID_SYM(fluid_settings_setnum);
|
||||||
|
DYN_FLUID_SYM(fluid_settings_setstr);
|
||||||
|
DYN_FLUID_SYM(fluid_settings_setint);
|
||||||
|
DYN_FLUID_SYM(fluid_settings_getstr);
|
||||||
|
DYN_FLUID_SYM(fluid_settings_getint);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_set_reverb_on);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_set_chorus_on);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_set_interp_method);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_set_polyphony);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_get_polyphony);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_get_active_voice_count);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_get_cpu_load);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_system_reset);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_noteon);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_noteoff);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_cc);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_program_change);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_channel_pressure);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_pitch_bend);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_write_float);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_sfload);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_set_reverb);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_set_chorus);
|
||||||
|
DYN_FLUID_SYM(fluid_synth_sysex);
|
||||||
|
|
||||||
bool FluidSynthMIDIDevice::LoadFluidSynth()
|
bool FluidSynthMIDIDevice::LoadFluidSynth()
|
||||||
{
|
{
|
||||||
LibFunc imports[] =
|
|
||||||
{
|
|
||||||
{ (void **)&new_fluid_settings, "new_fluid_settings" },
|
|
||||||
{ (void **)&new_fluid_synth, "new_fluid_synth" },
|
|
||||||
{ (void **)&delete_fluid_synth, "delete_fluid_synth" },
|
|
||||||
{ (void **)&delete_fluid_settings, "delete_fluid_settings" },
|
|
||||||
{ (void **)&fluid_settings_setnum, "fluid_settings_setnum" },
|
|
||||||
{ (void **)&fluid_settings_setstr, "fluid_settings_setstr" },
|
|
||||||
{ (void **)&fluid_settings_setint, "fluid_settings_setint" },
|
|
||||||
{ (void **)&fluid_settings_getstr, "fluid_settings_getstr" },
|
|
||||||
{ (void **)&fluid_settings_getint, "fluid_settings_getint" },
|
|
||||||
{ (void **)&fluid_synth_set_reverb_on, "fluid_synth_set_reverb_on" },
|
|
||||||
{ (void **)&fluid_synth_set_chorus_on, "fluid_synth_set_chorus_on" },
|
|
||||||
{ (void **)&fluid_synth_set_interp_method, "fluid_synth_set_interp_method" },
|
|
||||||
{ (void **)&fluid_synth_set_polyphony, "fluid_synth_set_polyphony" },
|
|
||||||
{ (void **)&fluid_synth_get_polyphony, "fluid_synth_get_polyphony" },
|
|
||||||
{ (void **)&fluid_synth_get_active_voice_count, "fluid_synth_get_active_voice_count" },
|
|
||||||
{ (void **)&fluid_synth_get_cpu_load, "fluid_synth_get_cpu_load" },
|
|
||||||
{ (void **)&fluid_synth_system_reset, "fluid_synth_system_reset" },
|
|
||||||
{ (void **)&fluid_synth_noteon, "fluid_synth_noteon" },
|
|
||||||
{ (void **)&fluid_synth_noteoff, "fluid_synth_noteoff" },
|
|
||||||
{ (void **)&fluid_synth_cc, "fluid_synth_cc" },
|
|
||||||
{ (void **)&fluid_synth_program_change, "fluid_synth_program_change" },
|
|
||||||
{ (void **)&fluid_synth_channel_pressure, "fluid_synth_channel_pressure" },
|
|
||||||
{ (void **)&fluid_synth_pitch_bend, "fluid_synth_pitch_bend" },
|
|
||||||
{ (void **)&fluid_synth_write_float, "fluid_synth_write_float" },
|
|
||||||
{ (void **)&fluid_synth_sfload, "fluid_synth_sfload" },
|
|
||||||
{ (void **)&fluid_synth_set_reverb, "fluid_synth_set_reverb" },
|
|
||||||
{ (void **)&fluid_synth_set_chorus, "fluid_synth_set_chorus" },
|
|
||||||
{ (void **)&fluid_synth_sysex, "fluid_synth_sysex" },
|
|
||||||
};
|
|
||||||
int fail = 0;
|
|
||||||
const char *libname;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (strlen(fluid_lib) > 0)
|
if (strlen(fluid_lib) > 0)
|
||||||
{
|
{
|
||||||
FluidSynthDLL = LoadLibrary(libname = fluid_lib);
|
if(!FluidSynthModule.Load({fluid_lib}))
|
||||||
if (nullptr == FluidSynthDLL)
|
|
||||||
{
|
{
|
||||||
|
const char* libname = fluid_lib;
|
||||||
Printf(TEXTCOLOR_RED "Could not load %s\n", libname);
|
Printf(TEXTCOLOR_RED "Could not load %s\n", libname);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
return true;
|
||||||
FluidSynthDLL = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nullptr == FluidSynthDLL)
|
#ifdef FLUIDSYNTHLIB2
|
||||||
{
|
if(!FluidSynthModule.Load({FLUIDSYNTHLIB1, FLUIDSYNTHLIB2}))
|
||||||
FluidSynthDLL = LoadLibrary(libname = FLUIDSYNTHLIB1);
|
|
||||||
if (nullptr == FluidSynthDLL)
|
|
||||||
{
|
|
||||||
FluidSynthDLL = LoadLibrary(libname = FLUIDSYNTHLIB2);
|
|
||||||
if (nullptr == FluidSynthDLL)
|
|
||||||
{
|
{
|
||||||
Printf(TEXTCOLOR_RED "Could not load " FLUIDSYNTHLIB1 " or " FLUIDSYNTHLIB2 "\n");
|
Printf(TEXTCOLOR_RED "Could not load " FLUIDSYNTHLIB1 " or " FLUIDSYNTHLIB2 "\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
if (strlen(fluid_lib) > 0)
|
if(!FluidSynthModule.Load({fluid_lib, FLUIDSYNTHLIB1}))
|
||||||
{
|
{
|
||||||
FluidSynthSO = dlopen(libname = fluid_lib, RTLD_LAZY);
|
Printf(TEXTCOLOR_RED "Could not load " FLUIDSYNTHLIB1 ": %s\n", dlerror());
|
||||||
if (nullptr == FluidSynthSO)
|
|
||||||
{
|
|
||||||
Printf(TEXTCOLOR_RED "Could not load %s: %s\n", libname, dlerror());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FluidSynthSO = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nullptr == FluidSynthSO)
|
|
||||||
{
|
|
||||||
FluidSynthSO = dlopen(libname = FLUIDSYNTHLIB, RTLD_LAZY);
|
|
||||||
if (nullptr == FluidSynthSO)
|
|
||||||
{
|
|
||||||
Printf(TEXTCOLOR_RED "Could not load " FLUIDSYNTHLIB ": %s\n", dlerror());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (size_t i = 0; i < countof(imports); ++i)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
FARPROC proc = GetProcAddress(FluidSynthDLL, imports[i].FuncName);
|
|
||||||
#else
|
|
||||||
void *proc = dlsym(FluidSynthSO, imports[i].FuncName);
|
|
||||||
#endif
|
|
||||||
if (proc == NULL)
|
|
||||||
{
|
|
||||||
Printf(TEXTCOLOR_RED"Failed to find %s in %s\n", imports[i].FuncName, libname);
|
|
||||||
fail++;
|
|
||||||
}
|
|
||||||
*imports[i].FuncPointer = (void *)proc;
|
|
||||||
}
|
|
||||||
if (fail == 0)
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
FreeLibrary(FluidSynthDLL);
|
|
||||||
FluidSynthDLL = NULL;
|
|
||||||
#else
|
|
||||||
dlclose(FluidSynthSO);
|
|
||||||
FluidSynthSO = NULL;
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
|
@ -786,19 +721,7 @@ bool FluidSynthMIDIDevice::LoadFluidSynth()
|
||||||
|
|
||||||
void FluidSynthMIDIDevice::UnloadFluidSynth()
|
void FluidSynthMIDIDevice::UnloadFluidSynth()
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
FluidSynthModule.Unload();
|
||||||
if (FluidSynthDLL != NULL)
|
|
||||||
{
|
|
||||||
FreeLibrary(FluidSynthDLL);
|
|
||||||
FluidSynthDLL = NULL;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (FluidSynthSO != NULL)
|
|
||||||
{
|
|
||||||
dlclose(FluidSynthSO);
|
|
||||||
FluidSynthSO = NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3,24 +3,9 @@
|
||||||
|
|
||||||
#if !defined NO_OPENAL && defined DYN_OPENAL
|
#if !defined NO_OPENAL && defined DYN_OPENAL
|
||||||
|
|
||||||
#ifndef _WIN32
|
#define DEFINE_ENTRY(type, name) static TReqProc<OpenALModule, type> p_##name{#name};
|
||||||
typedef void* FARPROC;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define DEFINE_ENTRY(type, name) static type p_##name;
|
|
||||||
#include "oaldef.h"
|
#include "oaldef.h"
|
||||||
#undef DEFINE_ENTRY
|
#undef DEFINE_ENTRY
|
||||||
struct oalloadentry
|
|
||||||
{
|
|
||||||
const char *name;
|
|
||||||
FARPROC *funcaddr;
|
|
||||||
};
|
|
||||||
static oalloadentry oalfuncs[] = {
|
|
||||||
#define DEFINE_ENTRY(type, name) { #name, (FARPROC*)&p_##name },
|
|
||||||
#include "oaldef.h"
|
|
||||||
#undef DEFINE_ENTRY
|
|
||||||
{ NULL, 0 }
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifndef IN_IDE_PARSER
|
#ifndef IN_IDE_PARSER
|
||||||
#define alEnable p_alEnable
|
#define alEnable p_alEnable
|
||||||
|
|
|
@ -55,29 +55,25 @@
|
||||||
#include "actor.h"
|
#include "actor.h"
|
||||||
#include "r_state.h"
|
#include "r_state.h"
|
||||||
#include "w_wad.h"
|
#include "w_wad.h"
|
||||||
|
#include "i_module.h"
|
||||||
#include "i_music.h"
|
#include "i_music.h"
|
||||||
#include "i_musicinterns.h"
|
#include "i_musicinterns.h"
|
||||||
#include "tempfiles.h"
|
#include "tempfiles.h"
|
||||||
|
|
||||||
|
FModule OpenALModule{"OpenAL"};
|
||||||
|
|
||||||
#include "oalload.h"
|
#include "oalload.h"
|
||||||
|
|
||||||
CVAR (String, snd_aldevice, "Default", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
CVAR (String, snd_aldevice, "Default", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||||
CVAR (Bool, snd_efx, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
CVAR (Bool, snd_efx, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static HMODULE hmodOpenAL;
|
|
||||||
#define OPENALLIB "openal32.dll"
|
#define OPENALLIB "openal32.dll"
|
||||||
#else
|
#elif defined(__APPLE__)
|
||||||
static void* hmodOpenAL;
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#define OPENALLIB "OpenAL.framework/OpenAL"
|
#define OPENALLIB "OpenAL.framework/OpenAL"
|
||||||
#else
|
#else
|
||||||
#define OPENALLIB "libopenal.so.1"
|
#define OPENALLIB "libopenal.so.1"
|
||||||
#endif
|
#endif
|
||||||
#define LoadLibrary(x) dlopen((x), RTLD_LAZY)
|
|
||||||
#define GetProcAddress(a,b) dlsym((a),(b))
|
|
||||||
#define FreeLibrary(x) dlclose((x))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool IsOpenALPresent()
|
bool IsOpenALPresent()
|
||||||
{
|
{
|
||||||
|
@ -92,29 +88,7 @@ bool IsOpenALPresent()
|
||||||
if (!done)
|
if (!done)
|
||||||
{
|
{
|
||||||
done = true;
|
done = true;
|
||||||
if (hmodOpenAL == NULL)
|
cached_result = OpenALModule.Load({NicePath("$PROGDIR/" OPENALLIB), OPENALLIB});
|
||||||
{
|
|
||||||
hmodOpenAL = LoadLibrary(NicePath("$PROGDIR/" OPENALLIB));
|
|
||||||
if (hmodOpenAL == NULL)
|
|
||||||
{
|
|
||||||
hmodOpenAL = LoadLibrary(OPENALLIB);
|
|
||||||
if (hmodOpenAL == NULL)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(int i = 0; oalfuncs[i].name != NULL; i++)
|
|
||||||
{
|
|
||||||
*oalfuncs[i].funcaddr = GetProcAddress(hmodOpenAL, oalfuncs[i].name);
|
|
||||||
if (*oalfuncs[i].funcaddr == NULL)
|
|
||||||
{
|
|
||||||
FreeLibrary(hmodOpenAL);
|
|
||||||
hmodOpenAL = NULL;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cached_result = true;
|
|
||||||
}
|
}
|
||||||
return cached_result;
|
return cached_result;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -68,6 +68,7 @@
|
||||||
#include "doomtype.h"
|
#include "doomtype.h"
|
||||||
#include "m_argv.h"
|
#include "m_argv.h"
|
||||||
#include "d_main.h"
|
#include "d_main.h"
|
||||||
|
#include "i_module.h"
|
||||||
#include "i_system.h"
|
#include "i_system.h"
|
||||||
#include "c_console.h"
|
#include "c_console.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
@ -84,6 +85,8 @@
|
||||||
#include "stats.h"
|
#include "stats.h"
|
||||||
#include "st_start.h"
|
#include "st_start.h"
|
||||||
|
|
||||||
|
#include "optwin32.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
// MACROS ------------------------------------------------------------------
|
// MACROS ------------------------------------------------------------------
|
||||||
|
@ -143,6 +146,21 @@ LONG GameTitleFontHeight;
|
||||||
LONG DefaultGUIFontHeight;
|
LONG DefaultGUIFontHeight;
|
||||||
LONG ErrorIconChar;
|
LONG ErrorIconChar;
|
||||||
|
|
||||||
|
FModule Kernel32Module{"Kernel32"};
|
||||||
|
FModule Shell32Module{"Shell32"};
|
||||||
|
FModule User32Module{"User32"};
|
||||||
|
|
||||||
|
namespace OptWin32 {
|
||||||
|
#define DYN_WIN32_SYM(x) decltype(x) x{#x}
|
||||||
|
|
||||||
|
DYN_WIN32_SYM(SHGetFolderPathA);
|
||||||
|
DYN_WIN32_SYM(SHGetKnownFolderPath);
|
||||||
|
DYN_WIN32_SYM(GetLongPathNameA);
|
||||||
|
DYN_WIN32_SYM(GetMonitorInfoA);
|
||||||
|
|
||||||
|
#undef DYN_WIN32_SYM
|
||||||
|
} // namespace OptWin32
|
||||||
|
|
||||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||||
|
|
||||||
static const char WinClassName[] = GAMENAME "MainWindow";
|
static const char WinClassName[] = GAMENAME "MainWindow";
|
||||||
|
@ -818,6 +836,11 @@ void DoMain (HINSTANCE hInstance)
|
||||||
|
|
||||||
Args = new DArgs(__argc, __argv);
|
Args = new DArgs(__argc, __argv);
|
||||||
|
|
||||||
|
// Load Win32 modules
|
||||||
|
Kernel32Module.Load({"kernel32.dll"});
|
||||||
|
Shell32Module.Load({"shell32.dll"});
|
||||||
|
User32Module.Load({"user32.dll"});
|
||||||
|
|
||||||
// Under XP, get our session ID so we can know when the user changes/locks sessions.
|
// Under XP, get our session ID so we can know when the user changes/locks sessions.
|
||||||
// Since we need to remain binary compatible with older versions of Windows, we
|
// Since we need to remain binary compatible with older versions of Windows, we
|
||||||
// need to extract the ProcessIdToSessionId function from kernel32.dll manually.
|
// need to extract the ProcessIdToSessionId function from kernel32.dll manually.
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
#include "version.h" // for GAMENAME
|
#include "version.h" // for GAMENAME
|
||||||
#include "i_system.h"
|
#include "i_system.h"
|
||||||
|
|
||||||
typedef HRESULT (WINAPI *GKFP)(REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR *);
|
#include "optwin32.h"
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
//
|
//
|
||||||
|
@ -94,19 +94,17 @@ bool UseKnownFolders()
|
||||||
|
|
||||||
bool GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create, FString &path)
|
bool GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create, FString &path)
|
||||||
{
|
{
|
||||||
static TOptWin32Proc<GKFP> SHGetKnownFolderPath("shell32.dll", "SHGetKnownFolderPath");
|
using OptWin32::SHGetFolderPathA;
|
||||||
|
using OptWin32::SHGetKnownFolderPath;
|
||||||
|
|
||||||
char pathstr[MAX_PATH];
|
char pathstr[MAX_PATH];
|
||||||
|
|
||||||
// SHGetKnownFolderPath knows about more folders than SHGetFolderPath, but is
|
// SHGetKnownFolderPath knows about more folders than SHGetFolderPath, but is
|
||||||
// new to Vista, hence the reason we support both.
|
// new to Vista, hence the reason we support both.
|
||||||
if (SHGetKnownFolderPath == NULL)
|
if (!SHGetKnownFolderPath)
|
||||||
{
|
{
|
||||||
static TOptWin32Proc<HRESULT(WINAPI*)(HWND, int, HANDLE, DWORD, LPTSTR)>
|
|
||||||
SHGetFolderPathA("shell32.dll", "SHGetFolderPathA");
|
|
||||||
|
|
||||||
// NT4 doesn't even have this function.
|
// NT4 doesn't even have this function.
|
||||||
if (SHGetFolderPathA == NULL)
|
if (!SHGetFolderPathA)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (shell_folder < 0)
|
if (shell_folder < 0)
|
||||||
|
@ -117,7 +115,7 @@ bool GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create
|
||||||
{
|
{
|
||||||
shell_folder |= CSIDL_FLAG_CREATE;
|
shell_folder |= CSIDL_FLAG_CREATE;
|
||||||
}
|
}
|
||||||
if (FAILED(SHGetFolderPathA.Call(NULL, shell_folder, NULL, 0, pathstr)))
|
if (FAILED(SHGetFolderPathA(NULL, shell_folder, NULL, 0, pathstr)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -127,7 +125,7 @@ bool GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PWSTR wpath;
|
PWSTR wpath;
|
||||||
if (FAILED(SHGetKnownFolderPath.Call(known_folder, create ? KF_FLAG_CREATE : 0, NULL, &wpath)))
|
if (FAILED(SHGetKnownFolderPath(known_folder, create ? KF_FLAG_CREATE : 0, NULL, &wpath)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,8 @@
|
||||||
#include "textures/bitmap.h"
|
#include "textures/bitmap.h"
|
||||||
#include "textures/textures.h"
|
#include "textures/textures.h"
|
||||||
|
|
||||||
|
#include "optwin32.h"
|
||||||
|
|
||||||
// MACROS ------------------------------------------------------------------
|
// MACROS ------------------------------------------------------------------
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
@ -1716,20 +1718,19 @@ unsigned int I_MakeRNGSeed()
|
||||||
|
|
||||||
FString I_GetLongPathName(FString shortpath)
|
FString I_GetLongPathName(FString shortpath)
|
||||||
{
|
{
|
||||||
static TOptWin32Proc<DWORD (WINAPI*)(LPCTSTR, LPTSTR, DWORD)>
|
using OptWin32::GetLongPathNameA;
|
||||||
GetLongPathNameA("kernel32.dll", "GetLongPathNameA");
|
|
||||||
|
|
||||||
// Doesn't exist on NT4
|
// Doesn't exist on NT4
|
||||||
if (GetLongPathName == NULL)
|
if (!GetLongPathName)
|
||||||
return shortpath;
|
return shortpath;
|
||||||
|
|
||||||
DWORD buffsize = GetLongPathNameA.Call(shortpath.GetChars(), NULL, 0);
|
DWORD buffsize = GetLongPathNameA(shortpath.GetChars(), NULL, 0);
|
||||||
if (buffsize == 0)
|
if (buffsize == 0)
|
||||||
{ // nothing to change (it doesn't exist, maybe?)
|
{ // nothing to change (it doesn't exist, maybe?)
|
||||||
return shortpath;
|
return shortpath;
|
||||||
}
|
}
|
||||||
TCHAR *buff = new TCHAR[buffsize];
|
TCHAR *buff = new TCHAR[buffsize];
|
||||||
DWORD buffsize2 = GetLongPathNameA.Call(shortpath.GetChars(), buff, buffsize);
|
DWORD buffsize2 = GetLongPathNameA(shortpath.GetChars(), buff, buffsize);
|
||||||
if (buffsize2 >= buffsize)
|
if (buffsize2 >= buffsize)
|
||||||
{ // Failure! Just return the short path
|
{ // Failure! Just return the short path
|
||||||
delete[] buff;
|
delete[] buff;
|
||||||
|
|
|
@ -51,30 +51,6 @@ typedef enum {
|
||||||
|
|
||||||
extern os_t OSPlatform;
|
extern os_t OSPlatform;
|
||||||
|
|
||||||
// Helper template so that we can access newer Win32 functions with a single static
|
|
||||||
// variable declaration. If this were C++11 it could be totally transparent.
|
|
||||||
template<typename Proto>
|
|
||||||
class TOptWin32Proc
|
|
||||||
{
|
|
||||||
static Proto GetOptionalWin32Proc(const char* module, const char* function)
|
|
||||||
{
|
|
||||||
HMODULE hmodule = GetModuleHandle(module);
|
|
||||||
if (hmodule == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return (Proto)GetProcAddress(hmodule, function);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
const Proto Call;
|
|
||||||
|
|
||||||
TOptWin32Proc(const char* module, const char* function)
|
|
||||||
: Call(GetOptionalWin32Proc(module, function)) {}
|
|
||||||
|
|
||||||
// Wrapper object can be tested against NULL, but not directly called.
|
|
||||||
operator const void*() const { return Call; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Called by DoomMain.
|
// Called by DoomMain.
|
||||||
void I_Init (void);
|
void I_Init (void);
|
||||||
|
|
||||||
|
|
24
src/win32/optwin32.h
Normal file
24
src/win32/optwin32.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Forward declarations for optional Win32 API procedures
|
||||||
|
// implemented in i_main.cpp
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#include <shlobj.h>
|
||||||
|
#define USE_WINDOWS_DWORD
|
||||||
|
|
||||||
|
#include "i_module.h"
|
||||||
|
|
||||||
|
extern FModule Kernel32Module;
|
||||||
|
extern FModule Shell32Module;
|
||||||
|
extern FModule User32Module;
|
||||||
|
|
||||||
|
namespace OptWin32 {
|
||||||
|
|
||||||
|
extern TOptProc<Shell32Module, HRESULT(WINAPI*)(HWND, int, HANDLE, DWORD, LPTSTR)> SHGetFolderPathA;
|
||||||
|
extern TOptProc<Shell32Module, HRESULT(WINAPI*)(REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR *)> SHGetKnownFolderPath;
|
||||||
|
extern TOptProc<Kernel32Module, DWORD (WINAPI*)(LPCTSTR, LPTSTR, DWORD)> GetLongPathNameA;
|
||||||
|
extern TOptProc<User32Module, BOOL(WINAPI*)(HMONITOR, LPMONITORINFO)> GetMonitorInfoA;
|
||||||
|
|
||||||
|
} // namespace OptWin32
|
|
@ -74,6 +74,8 @@
|
||||||
|
|
||||||
#include "win32iface.h"
|
#include "win32iface.h"
|
||||||
|
|
||||||
|
#include "optwin32.h"
|
||||||
|
|
||||||
// MACROS ------------------------------------------------------------------
|
// MACROS ------------------------------------------------------------------
|
||||||
|
|
||||||
// TYPES -------------------------------------------------------------------
|
// TYPES -------------------------------------------------------------------
|
||||||
|
@ -387,6 +389,8 @@ void Win32Video::BlankForGDI ()
|
||||||
|
|
||||||
void Win32Video::DumpAdapters()
|
void Win32Video::DumpAdapters()
|
||||||
{
|
{
|
||||||
|
using OptWin32::GetMonitorInfoA;
|
||||||
|
|
||||||
if (D3D == NULL)
|
if (D3D == NULL)
|
||||||
{
|
{
|
||||||
Printf("Multi-monitor support requires Direct3D.\n");
|
Printf("Multi-monitor support requires Direct3D.\n");
|
||||||
|
@ -415,9 +419,8 @@ void Win32Video::DumpAdapters()
|
||||||
MONITORINFOEX mi;
|
MONITORINFOEX mi;
|
||||||
mi.cbSize = sizeof(mi);
|
mi.cbSize = sizeof(mi);
|
||||||
|
|
||||||
TOptWin32Proc<BOOL(WINAPI*)(HMONITOR, LPMONITORINFO)> GetMonitorInfo("user32.dll", "GetMonitorInfoW");
|
assert(GetMonitorInfo); // Missing in NT4, but so is D3D
|
||||||
assert(GetMonitorInfo != NULL); // Missing in NT4, but so is D3D
|
if (GetMonitorInfo(hm, &mi))
|
||||||
if (GetMonitorInfo.Call(hm, &mi))
|
|
||||||
{
|
{
|
||||||
mysnprintf(moreinfo, countof(moreinfo), " [%ldx%ld @ (%ld,%ld)]%s",
|
mysnprintf(moreinfo, countof(moreinfo), " [%ldx%ld @ (%ld,%ld)]%s",
|
||||||
mi.rcMonitor.right - mi.rcMonitor.left,
|
mi.rcMonitor.right - mi.rcMonitor.left,
|
||||||
|
|
Loading…
Reference in a new issue