- 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:
Braden Obrzut 2016-11-12 18:32:09 -05:00
parent 0c0cb6d69c
commit 9342898361
16 changed files with 857 additions and 582 deletions

View file

@ -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,22 +214,37 @@ 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
if( NOT APPLE OR NOT OSX_COCOA_BACKEND )
find_package( SDL2 REQUIRED )
@ -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
View 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
View 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; }
};

View file

@ -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)

View file

@ -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 "";
}

View 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

View file

@ -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

View file

@ -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;
}
if (nullptr == FluidSynthDLL)
{
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;
else
return true;
}
if (nullptr == FluidSynthSO)
#ifdef FLUIDSYNTHLIB2
if(!FluidSynthModule.Load({FLUIDSYNTHLIB1, FLUIDSYNTHLIB2}))
{
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

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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;
}

View file

@ -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;

View file

@ -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
View 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

View file

@ -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,