mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-11-25 05:31:00 +00:00
- Added support for GTK3 (thanks to "MineyMe" and edward-san)
- Replaced GTK/OS X (note different from Cocoa) clipboard code with SDL clipboard API. - Removed requirement to link to GTK in order to compile with GTK support. - GTK is no longer init'd if the GTK IWAD picker is not used. - Our usage of GTK is such that the dynamic loader can work with both GTK2 and GTK3 depending on what's installed. - Since we're accumulating a lot of library loaders I've built a generic interface as FModule which replaces TOptWin32Proc and the loaders in the OpenAL and Fluidsynth code.
This commit is contained in:
parent
0c0cb6d69c
commit
9342898361
16 changed files with 857 additions and 582 deletions
|
@ -189,6 +189,7 @@ else()
|
|||
set( FMOD_INC_PATH_SUFFIXES PATH_SUFFIXES inc )
|
||||
set( FMOD_LIB_PATH_SUFFIXES PATH_SUFFIXES lib )
|
||||
set( NO_GTK ON )
|
||||
set( DYN_GTK OFF )
|
||||
|
||||
# Prevent inclusion of fp.h and FixMath.h from Carbon framework
|
||||
# Declarations from these files are not used but cause the following conflicts:
|
||||
|
@ -197,6 +198,7 @@ else()
|
|||
add_definitions( -D__FP__ -D__FIXMATH__ )
|
||||
else()
|
||||
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" )
|
||||
|
||||
set( FMOD_SEARCH_PATHS
|
||||
|
@ -212,20 +214,35 @@ else()
|
|||
|
||||
# Use GTK+ for the IWAD picker, if available.
|
||||
if( NOT NO_GTK )
|
||||
pkg_check_modules( GTK2 gtk+-2.0 )
|
||||
if( GTK2_FOUND )
|
||||
set( ZDOOM_LIBS ${ZDOOM_LIBS} ${GTK2_LIBRARIES} )
|
||||
include_directories( ${GTK2_INCLUDE_DIRS} )
|
||||
link_directories( ${GTK2_LIBRARY_DIRS} )
|
||||
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()
|
||||
set( NO_GTK ON )
|
||||
pkg_check_modules( GTK2 gtk+-2.0 )
|
||||
if( GTK2_FOUND )
|
||||
if( NOT DYN_GTK )
|
||||
set( ZDOOM_LIBS ${ZDOOM_LIBS} ${GTK2_LIBRARIES} )
|
||||
endif()
|
||||
include_directories( ${GTK2_INCLUDE_DIRS} )
|
||||
link_directories( ${GTK2_LIBRARY_DIRS} )
|
||||
else()
|
||||
set( NO_GTK ON )
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
set( NASM_NAMES nasm )
|
||||
|
||||
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()
|
||||
|
||||
# Non-Windows version also needs SDL except native OS X backend
|
||||
|
@ -688,7 +705,8 @@ set( PLAT_SDL_SOURCES
|
|||
posix/sdl/sdlvideo.cpp
|
||||
posix/sdl/st_start.cpp )
|
||||
set( PLAT_UNIX_SOURCES
|
||||
posix/unix/i_specialpaths.cpp )
|
||||
posix/unix/i_specialpaths.cpp
|
||||
posix/unix/iwadpicker_gtk.cpp )
|
||||
set( PLAT_OSX_SOURCES
|
||||
posix/osx/iwadpicker_cocoa.mm
|
||||
posix/osx/i_specialpaths.mm
|
||||
|
@ -1078,6 +1096,7 @@ set (PCH_SOURCES
|
|||
gi.cpp
|
||||
gitinfo.cpp
|
||||
hu_scores.cpp
|
||||
i_module.cpp
|
||||
i_net.cpp
|
||||
info.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 <new>
|
||||
#include <sys/param.h>
|
||||
#ifndef NO_GTK
|
||||
#include <gtk/gtk.h>
|
||||
#endif
|
||||
#include <locale.h>
|
||||
#if defined(__MACH__) && !defined(NOASM)
|
||||
#include <sys/types.h>
|
||||
|
@ -87,10 +84,6 @@ void Mac_I_FatalError(const char* errortext);
|
|||
|
||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||
|
||||
#ifndef NO_GTK
|
||||
bool GtkAvailable;
|
||||
#endif
|
||||
|
||||
// The command line arguments.
|
||||
DArgs *Args;
|
||||
|
||||
|
@ -259,10 +252,6 @@ int main (int argc, char **argv)
|
|||
// Note that the LANG environment variable is overridden by LC_*
|
||||
setenv ("LC_NUMERIC", "C", 1);
|
||||
|
||||
#ifndef NO_GTK
|
||||
GtkAvailable = gtk_init_check (&argc, &argv);
|
||||
#endif
|
||||
|
||||
setlocale (LC_ALL, "C");
|
||||
|
||||
if (SDL_Init (0) < 0)
|
||||
|
|
|
@ -34,10 +34,8 @@
|
|||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#ifndef NO_GTK
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#endif
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "doomerrors.h"
|
||||
#include <math.h>
|
||||
|
@ -71,10 +69,6 @@
|
|||
#include "m_fixed.h"
|
||||
#include "g_level.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#endif // __APPLE__
|
||||
|
||||
EXTERN_CVAR (String, language)
|
||||
|
||||
extern "C"
|
||||
|
@ -84,7 +78,8 @@ extern "C"
|
|||
}
|
||||
|
||||
#ifndef NO_GTK
|
||||
extern bool GtkAvailable;
|
||||
bool I_GtkAvailable ();
|
||||
int I_PickIWad_Gtk (WadStuff *wads, int numwads, bool showwin, int defaultiwad);
|
||||
#elif defined(__APPLE__)
|
||||
int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad);
|
||||
#endif
|
||||
|
@ -262,183 +257,6 @@ void I_PrintStr (const char *cp)
|
|||
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;
|
||||
|
@ -448,7 +266,7 @@ int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad)
|
|||
return defaultiwad;
|
||||
}
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
#ifndef __APPLE__
|
||||
const char *str;
|
||||
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
|
||||
|
||||
#ifndef NO_GTK
|
||||
if (GtkAvailable)
|
||||
if (I_GtkAvailable())
|
||||
{
|
||||
return I_PickIWad_Gtk (wads, numwads, showwin, defaultiwad);
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
return I_PickIWad_Cocoa (wads, numwads, showwin, defaultiwad);
|
||||
#endif
|
||||
|
||||
|
@ -605,139 +426,19 @@ int I_FindAttr (findstate_t *fileinfo)
|
|||
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)
|
||||
{
|
||||
#ifndef NO_GTK
|
||||
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
|
||||
SDL_SetClipboardText(str);
|
||||
}
|
||||
|
||||
FString I_GetFromClipboard (bool use_primary_selection)
|
||||
{
|
||||
#ifndef NO_GTK
|
||||
if (GtkAvailable)
|
||||
if(char *ret = SDL_GetClipboardText())
|
||||
{
|
||||
GtkClipboard *clipboard = gtk_clipboard_get(use_primary_selection ?
|
||||
GDK_SELECTION_PRIMARY : GDK_SELECTION_CLIPBOARD);
|
||||
if (clipboard != NULL)
|
||||
{
|
||||
gchar *text = gtk_clipboard_wait_for_text(clipboard);
|
||||
if (text != NULL)
|
||||
{
|
||||
FString copy(text);
|
||||
g_free(text);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
FString text(ret);
|
||||
SDL_free(ret);
|
||||
return text;
|
||||
}
|
||||
#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 "";
|
||||
}
|
||||
|
||||
|
|
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
|
||||
#include <fluidsynth.h>
|
||||
#else
|
||||
#include "i_module.h"
|
||||
extern FModule FluidSynthModule;
|
||||
|
||||
struct fluid_settings_t;
|
||||
struct fluid_synth_t;
|
||||
#endif
|
||||
|
@ -386,40 +389,35 @@ protected:
|
|||
|
||||
#ifdef DYN_FLUIDSYNTH
|
||||
enum { FLUID_FAILED = -1, FLUID_OK = 0 };
|
||||
fluid_settings_t *(*new_fluid_settings)();
|
||||
fluid_synth_t *(*new_fluid_synth)(fluid_settings_t *);
|
||||
int (*delete_fluid_synth)(fluid_synth_t *);
|
||||
void (*delete_fluid_settings)(fluid_settings_t *);
|
||||
int (*fluid_settings_setnum)(fluid_settings_t *, const char *, double);
|
||||
int (*fluid_settings_setstr)(fluid_settings_t *, const char *, const char *);
|
||||
int (*fluid_settings_setint)(fluid_settings_t *, const char *, int);
|
||||
int (*fluid_settings_getstr)(fluid_settings_t *, const char *, char **);
|
||||
int (*fluid_settings_getint)(fluid_settings_t *, const char *, int *);
|
||||
void (*fluid_synth_set_reverb_on)(fluid_synth_t *, int);
|
||||
void (*fluid_synth_set_chorus_on)(fluid_synth_t *, int);
|
||||
int (*fluid_synth_set_interp_method)(fluid_synth_t *, int, int);
|
||||
int (*fluid_synth_set_polyphony)(fluid_synth_t *, int);
|
||||
int (*fluid_synth_get_polyphony)(fluid_synth_t *);
|
||||
int (*fluid_synth_get_active_voice_count)(fluid_synth_t *);
|
||||
double (*fluid_synth_get_cpu_load)(fluid_synth_t *);
|
||||
int (*fluid_synth_system_reset)(fluid_synth_t *);
|
||||
int (*fluid_synth_noteon)(fluid_synth_t *, int, int, int);
|
||||
int (*fluid_synth_noteoff)(fluid_synth_t *, int, int);
|
||||
int (*fluid_synth_cc)(fluid_synth_t *, int, int, int);
|
||||
int (*fluid_synth_program_change)(fluid_synth_t *, int, int);
|
||||
int (*fluid_synth_channel_pressure)(fluid_synth_t *, int, int);
|
||||
int (*fluid_synth_pitch_bend)(fluid_synth_t *, int, int);
|
||||
int (*fluid_synth_write_float)(fluid_synth_t *, int, void *, int, int, void *, int, int);
|
||||
int (*fluid_synth_sfload)(fluid_synth_t *, const char *, int);
|
||||
void (*fluid_synth_set_reverb)(fluid_synth_t *, double, double, double, double);
|
||||
void (*fluid_synth_set_chorus)(fluid_synth_t *, int, double, double, double, int);
|
||||
int (*fluid_synth_sysex)(fluid_synth_t *, const char *, int, char *, int *, int *, int);
|
||||
static TReqProc<FluidSynthModule, fluid_settings_t *(*)()> new_fluid_settings;
|
||||
static TReqProc<FluidSynthModule, fluid_synth_t *(*)(fluid_settings_t *)> new_fluid_synth;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *)> delete_fluid_synth;
|
||||
static TReqProc<FluidSynthModule, void (*)(fluid_settings_t *)> delete_fluid_settings;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_settings_t *, const char *, double)> fluid_settings_setnum;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_settings_t *, const char *, const char *)> fluid_settings_setstr;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_settings_t *, const char *, int)> fluid_settings_setint;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_settings_t *, const char *, char **)> fluid_settings_getstr;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_settings_t *, const char *, int *)> fluid_settings_getint;
|
||||
static TReqProc<FluidSynthModule, void (*)(fluid_synth_t *, int)> fluid_synth_set_reverb_on;
|
||||
static TReqProc<FluidSynthModule, void (*)(fluid_synth_t *, int)> fluid_synth_set_chorus_on;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int, int)> fluid_synth_set_interp_method;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int)> fluid_synth_set_polyphony;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *)> fluid_synth_get_polyphony;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *)> fluid_synth_get_active_voice_count;
|
||||
static TReqProc<FluidSynthModule, double (*)(fluid_synth_t *)> fluid_synth_get_cpu_load;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *)> fluid_synth_system_reset;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int, int, int)> fluid_synth_noteon;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int, int)> fluid_synth_noteoff;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int, int, int)> fluid_synth_cc;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int, int)> fluid_synth_program_change;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int, int)> fluid_synth_channel_pressure;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int, int)> fluid_synth_pitch_bend;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, int, void *, int, int, void *, int, int)> fluid_synth_write_float;
|
||||
static TReqProc<FluidSynthModule, int (*)(fluid_synth_t *, const char *, int)> fluid_synth_sfload;
|
||||
static TReqProc<FluidSynthModule, void (*)(fluid_synth_t *, double, double, double, double)> fluid_synth_set_reverb;
|
||||
static TReqProc<FluidSynthModule, void (*)(fluid_synth_t *, int, double, double, double, int)> fluid_synth_set_chorus;
|
||||
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();
|
||||
void UnloadFluidSynth();
|
||||
#endif
|
||||
|
|
|
@ -60,9 +60,9 @@
|
|||
#include <dlfcn.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define FLUIDSYNTHLIB "libfluidsynth.1.dylib"
|
||||
#define FLUIDSYNTHLIB1 "libfluidsynth.1.dylib"
|
||||
#else // !__APPLE__
|
||||
#define FLUIDSYNTHLIB "libfluidsynth.so.1"
|
||||
#define FLUIDSYNTHLIB1 "libfluidsynth.so.1"
|
||||
#endif // __APPLE__
|
||||
#endif
|
||||
|
||||
|
@ -644,12 +644,6 @@ FString FluidSynthMIDIDevice::GetStats()
|
|||
|
||||
#ifdef DYN_FLUIDSYNTH
|
||||
|
||||
struct LibFunc
|
||||
{
|
||||
void **FuncPointer;
|
||||
const char *FuncName;
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FluidSynthMIDIDevice :: LoadFluidSynth
|
||||
|
@ -658,124 +652,65 @@ 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()
|
||||
{
|
||||
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)
|
||||
{
|
||||
FluidSynthDLL = LoadLibrary(libname = fluid_lib);
|
||||
if (nullptr == FluidSynthDLL)
|
||||
if(!FluidSynthModule.Load({fluid_lib}))
|
||||
{
|
||||
const char* libname = fluid_lib;
|
||||
Printf(TEXTCOLOR_RED "Could not load %s\n", libname);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FluidSynthDLL = nullptr;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
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");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (strlen(fluid_lib) > 0)
|
||||
{
|
||||
FluidSynthSO = dlopen(libname = fluid_lib, RTLD_LAZY);
|
||||
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;
|
||||
}
|
||||
}
|
||||
#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;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef _WIN32
|
||||
FreeLibrary(FluidSynthDLL);
|
||||
FluidSynthDLL = NULL;
|
||||
#else
|
||||
dlclose(FluidSynthSO);
|
||||
FluidSynthSO = NULL;
|
||||
#endif
|
||||
Printf(TEXTCOLOR_RED "Could not load " FLUIDSYNTHLIB1 " or " FLUIDSYNTHLIB2 "\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
if(!FluidSynthModule.Load({fluid_lib, FLUIDSYNTHLIB1}))
|
||||
{
|
||||
Printf(TEXTCOLOR_RED "Could not load " FLUIDSYNTHLIB1 ": %s\n", dlerror());
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -786,19 +721,7 @@ bool FluidSynthMIDIDevice::LoadFluidSynth()
|
|||
|
||||
void FluidSynthMIDIDevice::UnloadFluidSynth()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (FluidSynthDLL != NULL)
|
||||
{
|
||||
FreeLibrary(FluidSynthDLL);
|
||||
FluidSynthDLL = NULL;
|
||||
}
|
||||
#else
|
||||
if (FluidSynthSO != NULL)
|
||||
{
|
||||
dlclose(FluidSynthSO);
|
||||
FluidSynthSO = NULL;
|
||||
}
|
||||
#endif
|
||||
FluidSynthModule.Unload();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,24 +3,9 @@
|
|||
|
||||
#if !defined NO_OPENAL && defined DYN_OPENAL
|
||||
|
||||
#ifndef _WIN32
|
||||
typedef void* FARPROC;
|
||||
#endif
|
||||
|
||||
#define DEFINE_ENTRY(type, name) static type p_##name;
|
||||
#define DEFINE_ENTRY(type, name) static TReqProc<OpenALModule, type> p_##name{#name};
|
||||
#include "oaldef.h"
|
||||
#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
|
||||
#define alEnable p_alEnable
|
||||
|
|
|
@ -55,29 +55,25 @@
|
|||
#include "actor.h"
|
||||
#include "r_state.h"
|
||||
#include "w_wad.h"
|
||||
#include "i_module.h"
|
||||
#include "i_music.h"
|
||||
#include "i_musicinterns.h"
|
||||
#include "tempfiles.h"
|
||||
|
||||
FModule OpenALModule{"OpenAL"};
|
||||
|
||||
#include "oalload.h"
|
||||
|
||||
CVAR (String, snd_aldevice, "Default", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
CVAR (Bool, snd_efx, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
|
||||
#ifdef _WIN32
|
||||
static HMODULE hmodOpenAL;
|
||||
#define OPENALLIB "openal32.dll"
|
||||
#else
|
||||
static void* hmodOpenAL;
|
||||
#ifdef __APPLE__
|
||||
#elif defined(__APPLE__)
|
||||
#define OPENALLIB "OpenAL.framework/OpenAL"
|
||||
#else
|
||||
#define OPENALLIB "libopenal.so.1"
|
||||
#endif
|
||||
#define LoadLibrary(x) dlopen((x), RTLD_LAZY)
|
||||
#define GetProcAddress(a,b) dlsym((a),(b))
|
||||
#define FreeLibrary(x) dlclose((x))
|
||||
#endif
|
||||
|
||||
bool IsOpenALPresent()
|
||||
{
|
||||
|
@ -92,29 +88,7 @@ bool IsOpenALPresent()
|
|||
if (!done)
|
||||
{
|
||||
done = true;
|
||||
if (hmodOpenAL == NULL)
|
||||
{
|
||||
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;
|
||||
cached_result = OpenALModule.Load({NicePath("$PROGDIR/" OPENALLIB), OPENALLIB});
|
||||
}
|
||||
return cached_result;
|
||||
#endif
|
||||
|
|
|
@ -68,6 +68,7 @@
|
|||
#include "doomtype.h"
|
||||
#include "m_argv.h"
|
||||
#include "d_main.h"
|
||||
#include "i_module.h"
|
||||
#include "i_system.h"
|
||||
#include "c_console.h"
|
||||
#include "version.h"
|
||||
|
@ -84,6 +85,8 @@
|
|||
#include "stats.h"
|
||||
#include "st_start.h"
|
||||
|
||||
#include "optwin32.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
@ -143,6 +146,21 @@ LONG GameTitleFontHeight;
|
|||
LONG DefaultGUIFontHeight;
|
||||
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 ------------------------------------------------
|
||||
|
||||
static const char WinClassName[] = GAMENAME "MainWindow";
|
||||
|
@ -818,6 +836,11 @@ void DoMain (HINSTANCE hInstance)
|
|||
|
||||
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.
|
||||
// Since we need to remain binary compatible with older versions of Windows, we
|
||||
// need to extract the ProcessIdToSessionId function from kernel32.dll manually.
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
#include "version.h" // for GAMENAME
|
||||
#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)
|
||||
{
|
||||
static TOptWin32Proc<GKFP> SHGetKnownFolderPath("shell32.dll", "SHGetKnownFolderPath");
|
||||
using OptWin32::SHGetFolderPathA;
|
||||
using OptWin32::SHGetKnownFolderPath;
|
||||
|
||||
char pathstr[MAX_PATH];
|
||||
|
||||
// SHGetKnownFolderPath knows about more folders than SHGetFolderPath, but is
|
||||
// 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.
|
||||
if (SHGetFolderPathA == NULL)
|
||||
if (!SHGetFolderPathA)
|
||||
return false;
|
||||
|
||||
if (shell_folder < 0)
|
||||
|
@ -117,7 +115,7 @@ bool GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool 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;
|
||||
}
|
||||
|
@ -127,7 +125,7 @@ bool GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create
|
|||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -86,6 +86,8 @@
|
|||
#include "textures/bitmap.h"
|
||||
#include "textures/textures.h"
|
||||
|
||||
#include "optwin32.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
@ -1716,20 +1718,19 @@ unsigned int I_MakeRNGSeed()
|
|||
|
||||
FString I_GetLongPathName(FString shortpath)
|
||||
{
|
||||
static TOptWin32Proc<DWORD (WINAPI*)(LPCTSTR, LPTSTR, DWORD)>
|
||||
GetLongPathNameA("kernel32.dll", "GetLongPathNameA");
|
||||
using OptWin32::GetLongPathNameA;
|
||||
|
||||
// Doesn't exist on NT4
|
||||
if (GetLongPathName == NULL)
|
||||
if (!GetLongPathName)
|
||||
return shortpath;
|
||||
|
||||
DWORD buffsize = GetLongPathNameA.Call(shortpath.GetChars(), NULL, 0);
|
||||
DWORD buffsize = GetLongPathNameA(shortpath.GetChars(), NULL, 0);
|
||||
if (buffsize == 0)
|
||||
{ // nothing to change (it doesn't exist, maybe?)
|
||||
return shortpath;
|
||||
}
|
||||
TCHAR *buff = new TCHAR[buffsize];
|
||||
DWORD buffsize2 = GetLongPathNameA.Call(shortpath.GetChars(), buff, buffsize);
|
||||
DWORD buffsize2 = GetLongPathNameA(shortpath.GetChars(), buff, buffsize);
|
||||
if (buffsize2 >= buffsize)
|
||||
{ // Failure! Just return the short path
|
||||
delete[] buff;
|
||||
|
|
|
@ -51,30 +51,6 @@ typedef enum {
|
|||
|
||||
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.
|
||||
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 "optwin32.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
// TYPES -------------------------------------------------------------------
|
||||
|
@ -387,6 +389,8 @@ void Win32Video::BlankForGDI ()
|
|||
|
||||
void Win32Video::DumpAdapters()
|
||||
{
|
||||
using OptWin32::GetMonitorInfoA;
|
||||
|
||||
if (D3D == NULL)
|
||||
{
|
||||
Printf("Multi-monitor support requires Direct3D.\n");
|
||||
|
@ -415,9 +419,8 @@ void Win32Video::DumpAdapters()
|
|||
MONITORINFOEX mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
|
||||
TOptWin32Proc<BOOL(WINAPI*)(HMONITOR, LPMONITORINFO)> GetMonitorInfo("user32.dll", "GetMonitorInfoW");
|
||||
assert(GetMonitorInfo != NULL); // Missing in NT4, but so is D3D
|
||||
if (GetMonitorInfo.Call(hm, &mi))
|
||||
assert(GetMonitorInfo); // Missing in NT4, but so is D3D
|
||||
if (GetMonitorInfo(hm, &mi))
|
||||
{
|
||||
mysnprintf(moreinfo, countof(moreinfo), " [%ldx%ld @ (%ld,%ld)]%s",
|
||||
mi.rcMonitor.right - mi.rcMonitor.left,
|
||||
|
|
Loading…
Reference in a new issue