mirror of https://github.com/OpenTTD/OpenTTD
(svn r2391) - Feature: saving games happen in a seperate thread so you no longer will have to wait such a long time (especially handy on bigger maps and multiplayer games). The mouse also changes into the 'ZZZ' state :P. The thread on windows is currently given a little-bit-less-than-normal priority so it should not interfere that much with the gameplay; it will take a bit longer though. Upon the exit of the game any pending saves are waited upon.
- Fix: fixed GetSavegameFormat() so that it takes the best compressor (highest), or a forced one added with the parameter - Open issues: 1. Don't attempt to load a game while saving is in progress, it will kick you back to the intro-screen with only the vast ocean to look at. 2. The server is disabled from threaded-saving, but might be enabled in the future. 3. Current implementation only allows 1 additional running thread. 4. Stupid global variables.....grrr Big thanks for TrueLight and the amazing memorypool :Drelease/0.4.5
parent
f7dcd2e834
commit
a51cfd58b8
|
@ -266,4 +266,7 @@ void DeterminePaths(void);
|
||||||
char * CDECL str_fmt(const char *str, ...);
|
char * CDECL str_fmt(const char *str, ...);
|
||||||
|
|
||||||
void bubblesort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));
|
void bubblesort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));
|
||||||
|
bool CreateOTTDThread(void *func, void *param);
|
||||||
|
void CloseOTTDThread(void);
|
||||||
|
void JoinOTTDThread(void);
|
||||||
#endif /* FUNCTIONS_H */
|
#endif /* FUNCTIONS_H */
|
||||||
|
|
|
@ -857,6 +857,8 @@ STR_032C_3_AIR_SERVICE :{BLACK}3: Air s
|
||||||
STR_032D_4_SHIP_SERVICE :{BLACK}4: Ship service
|
STR_032D_4_SHIP_SERVICE :{BLACK}4: Ship service
|
||||||
STR_032E_5_RAILROAD_SERVICE_ADVANCED :{BLACK}5: Railway service (advanced)
|
STR_032E_5_RAILROAD_SERVICE_ADVANCED :{BLACK}5: Railway service (advanced)
|
||||||
STR_032F_AUTOSAVE :{RED}AUTOSAVE
|
STR_032F_AUTOSAVE :{RED}AUTOSAVE
|
||||||
|
STR_SAVING_GAME :{RED}* * SAVING GAME * *
|
||||||
|
STR_SAVE_STILL_IN_PROGRESS :{WHITE}Saving still in progress,{}please wait until it is finished!
|
||||||
STR_0330_SELECT_EZY_STREET_STYLE :{BLACK}Select 'Ezy Street style music' programme
|
STR_0330_SELECT_EZY_STREET_STYLE :{BLACK}Select 'Ezy Street style music' programme
|
||||||
|
|
||||||
STR_0335_6 :{BLACK}6
|
STR_0335_6 :{BLACK}6
|
||||||
|
|
32
main_gui.c
32
main_gui.c
|
@ -2207,24 +2207,24 @@ static bool DrawScrollingStatusText(NewsItem *ni, int pos)
|
||||||
|
|
||||||
static void StatusBarWndProc(Window *w, WindowEvent *e)
|
static void StatusBarWndProc(Window *w, WindowEvent *e)
|
||||||
{
|
{
|
||||||
Player *p;
|
switch (e->event) {
|
||||||
|
case WE_PAINT: {
|
||||||
|
const Player *p = (_local_player == OWNER_SPECTATOR) ? NULL : DEREF_PLAYER(_local_player);
|
||||||
|
|
||||||
switch(e->event) {
|
|
||||||
case WE_PAINT:
|
|
||||||
DrawWindowWidgets(w);
|
DrawWindowWidgets(w);
|
||||||
SetDParam(0, _date);
|
SetDParam(0, _date);
|
||||||
DrawStringCentered(70, 1, ((_pause||_patches.status_long_date)?STR_00AF:STR_00AE), 0);
|
DrawStringCentered(70, 1, ((_pause||_patches.status_long_date)?STR_00AF:STR_00AE), 0);
|
||||||
|
|
||||||
p = _local_player == OWNER_SPECTATOR ? NULL : DEREF_PLAYER(_local_player);
|
if (p != NULL) {
|
||||||
|
|
||||||
if (p) {
|
|
||||||
// Draw player money
|
// Draw player money
|
||||||
SetDParam64(0, p->money64);
|
SetDParam64(0, p->money64);
|
||||||
DrawStringCentered(570, 1, p->player_money >= 0 ? STR_0004 : STR_0005, 0);
|
DrawStringCentered(570, 1, p->player_money >= 0 ? STR_0004 : STR_0005, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw status bar
|
// Draw status bar
|
||||||
if (_do_autosave) {
|
if (w->message.msg) { // true when saving is active
|
||||||
|
DrawStringCentered(320, 1, STR_SAVING_GAME, 0);
|
||||||
|
} else if (_do_autosave) {
|
||||||
DrawStringCentered(320, 1, STR_032F_AUTOSAVE, 0);
|
DrawStringCentered(320, 1, STR_032F_AUTOSAVE, 0);
|
||||||
} else if (_pause) {
|
} else if (_pause) {
|
||||||
DrawStringCentered(320, 1, STR_0319_PAUSED, 0);
|
DrawStringCentered(320, 1, STR_0319_PAUSED, 0);
|
||||||
|
@ -2241,17 +2241,19 @@ static void StatusBarWndProc(Window *w, WindowEvent *e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WP(w, def_d).data_2 > 0)
|
if (WP(w, def_d).data_2 > 0) DrawSprite(SPR_BLOT | PALETTE_TO_RED, 489, 2);
|
||||||
DrawSprite(SPR_BLOT | PALETTE_TO_RED, 489, 2);
|
} break;
|
||||||
|
|
||||||
|
case WE_MESSAGE:
|
||||||
|
w->message.msg = e->message.msg;
|
||||||
|
SetWindowDirty(w);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WE_CLICK:
|
case WE_CLICK:
|
||||||
if (e->click.widget == 1) {
|
switch (e->click.widget) {
|
||||||
ShowLastNewsMessage();
|
case 1: ShowLastNewsMessage(); break;
|
||||||
} else if (e->click.widget == 2) {
|
case 2: if (_local_player != OWNER_SPECTATOR) ShowPlayerFinances(_local_player); break;
|
||||||
if (_local_player != OWNER_SPECTATOR) ShowPlayerFinances(_local_player);
|
default: ResetObjectToPlace();
|
||||||
} else {
|
|
||||||
ResetObjectToPlace();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
195
saveload.c
195
saveload.c
|
@ -817,6 +817,44 @@ static void UninitNoComp(void)
|
||||||
free(_sl.buf);
|
free(_sl.buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//********************************************
|
||||||
|
//********** START OF MEMORY CODE (in ram)****
|
||||||
|
//********************************************
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SAVELOAD_POOL_BLOCK_SIZE_BITS = 17,
|
||||||
|
SAVELOAD_POOL_MAX_BLOCKS = 500
|
||||||
|
};
|
||||||
|
|
||||||
|
/* A maximum size of of 128K * 500 = 64.000KB savegames */
|
||||||
|
static MemoryPool _saveload_pool = {"Savegame", SAVELOAD_POOL_MAX_BLOCKS, SAVELOAD_POOL_BLOCK_SIZE_BITS, sizeof(byte), NULL, 0, 0, NULL};
|
||||||
|
static uint _save_byte_count;
|
||||||
|
|
||||||
|
static bool InitMem(void)
|
||||||
|
{
|
||||||
|
CleanPool(&_saveload_pool);
|
||||||
|
AddBlockToPool(&_saveload_pool);
|
||||||
|
|
||||||
|
/* A block from the pool is a contigious area of memory, so it is safe to write to it sequentially */
|
||||||
|
_save_byte_count = 0;
|
||||||
|
_sl.bufsize = _saveload_pool.total_items;
|
||||||
|
_sl.buf = (byte*)GetItemFromPool(&_saveload_pool, _save_byte_count);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UnInitMem(void)
|
||||||
|
{
|
||||||
|
CleanPool(&_saveload_pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteMem(uint size)
|
||||||
|
{
|
||||||
|
_save_byte_count += size;
|
||||||
|
/* Allocate new block and new buffer-pointer */
|
||||||
|
AddBlockIfNeeded(&_saveload_pool, _save_byte_count);
|
||||||
|
_sl.buf = (byte*)GetItemFromPool(&_saveload_pool, _save_byte_count);
|
||||||
|
}
|
||||||
|
|
||||||
//********************************************
|
//********************************************
|
||||||
//********** START OF ZLIB CODE **************
|
//********** START OF ZLIB CODE **************
|
||||||
//********************************************
|
//********************************************
|
||||||
|
@ -1064,33 +1102,35 @@ typedef struct {
|
||||||
} SaveLoadFormat;
|
} SaveLoadFormat;
|
||||||
|
|
||||||
static const SaveLoadFormat _saveload_formats[] = {
|
static const SaveLoadFormat _saveload_formats[] = {
|
||||||
{"lzo", TO_BE32X('OTTD'), InitLZO, ReadLZO, UninitLZO, InitLZO, WriteLZO, UninitLZO},
|
{"memory", 0, NULL, NULL, NULL, InitMem, WriteMem, UnInitMem},
|
||||||
{"none", TO_BE32X('OTTN'), InitNoComp, ReadNoComp, UninitNoComp, InitNoComp, WriteNoComp, UninitNoComp},
|
{"lzo", TO_BE32X('OTTD'), InitLZO, ReadLZO, UninitLZO, InitLZO, WriteLZO, UninitLZO},
|
||||||
|
{"none", TO_BE32X('OTTN'), InitNoComp, ReadNoComp, UninitNoComp, InitNoComp, WriteNoComp, UninitNoComp},
|
||||||
#if defined(WITH_ZLIB)
|
#if defined(WITH_ZLIB)
|
||||||
{"zlib", TO_BE32X('OTTZ'), InitReadZlib, ReadZlib, UninitReadZlib, InitWriteZlib, WriteZlib, UninitWriteZlib},
|
{"zlib", TO_BE32X('OTTZ'), InitReadZlib, ReadZlib, UninitReadZlib, InitWriteZlib, WriteZlib, UninitWriteZlib},
|
||||||
#else
|
#else
|
||||||
{"zlib", TO_BE32X('OTTZ'), NULL, NULL, NULL, NULL, NULL, NULL}
|
{"zlib", TO_BE32X('OTTZ'), NULL, NULL, NULL, NULL, NULL, NULL},
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the savegameformat of the game. Whether it was create with ZLIB compression
|
* Return the savegameformat of the game. Whether it was create with ZLIB compression
|
||||||
* uncompressed, or another type
|
* uncompressed, or another type
|
||||||
* @param s Name of the savegame format
|
* @param s Name of the savegame format. If NULL it picks the first available one
|
||||||
* @return Pointer to @SaveLoadFormat struct giving all characteristics of this type of savegame
|
* @return Pointer to @SaveLoadFormat struct giving all characteristics of this type of savegame
|
||||||
*/
|
*/
|
||||||
static const SaveLoadFormat *GetSavegameFormat(const char *s)
|
static const SaveLoadFormat *GetSavegameFormat(const char *s)
|
||||||
{
|
{
|
||||||
const SaveLoadFormat *def = endof(_saveload_formats) - 1;
|
const SaveLoadFormat *def = endof(_saveload_formats) - 1;
|
||||||
int i;
|
|
||||||
|
|
||||||
// find default savegame format, the highest one with which files can be written
|
// find default savegame format, the highest one with which files can be written
|
||||||
while (!def->init_write) def--;
|
while (!def->init_write) def--;
|
||||||
|
|
||||||
if (_savegame_format[0]) {
|
if (s != NULL && s[0] != '\0') {
|
||||||
for (i = 0; i != lengthof(_saveload_formats); i++)
|
const SaveLoadFormat *slf;
|
||||||
if (_saveload_formats[i].init_write && !strcmp(s, _saveload_formats[i].name))
|
for (slf = &_saveload_formats[0]; slf != endof(_saveload_formats); slf++) {
|
||||||
return _saveload_formats + i;
|
if (slf->init_write != NULL && strcmp(s, slf->name) == 0)
|
||||||
|
return slf;
|
||||||
|
}
|
||||||
|
|
||||||
ShowInfoF("Savegame format '%s' is not available. Reverting to '%s'.", s, def->name);
|
ShowInfoF("Savegame format '%s' is not available. Reverting to '%s'.", s, def->name);
|
||||||
}
|
}
|
||||||
|
@ -1112,6 +1152,95 @@ static inline int AbortSaveLoad(void)
|
||||||
return SL_ERROR;
|
return SL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "network.h"
|
||||||
|
#include "table/strings.h"
|
||||||
|
#include "table/sprites.h"
|
||||||
|
#include "gfx.h"
|
||||||
|
#include "gui.h"
|
||||||
|
|
||||||
|
static bool _saving_game = false;
|
||||||
|
|
||||||
|
/** Update the gui accordingly when starting saving
|
||||||
|
* and set locks on saveload */
|
||||||
|
static inline void SaveFileStart(void)
|
||||||
|
{
|
||||||
|
SetMouseCursor(SPR_CURSOR_ZZZ);
|
||||||
|
SendWindowMessage(WC_STATUS_BAR, 0, true, 0, 0);
|
||||||
|
_saving_game = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Update the gui accordingly when saving is done and release locks
|
||||||
|
* on saveload */
|
||||||
|
static inline void SaveFileDone(void)
|
||||||
|
{
|
||||||
|
if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE);
|
||||||
|
SendWindowMessage(WC_STATUS_BAR, 0, false, 0, 0);
|
||||||
|
_saving_game = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** We have written the whole game into memory, _saveload_pool, now find
|
||||||
|
* and appropiate compressor and start writing to file.
|
||||||
|
*/
|
||||||
|
static bool SaveFileToDisk(void *ptr)
|
||||||
|
{
|
||||||
|
const SaveLoadFormat *fmt = GetSavegameFormat(_savegame_format);
|
||||||
|
/* XXX - backup _sl.buf cause it is used internally by the writer
|
||||||
|
* and we update it for our own purposes */
|
||||||
|
byte *tmp = _sl.buf;
|
||||||
|
uint32 hdr[2];
|
||||||
|
|
||||||
|
SaveFileStart();
|
||||||
|
|
||||||
|
/* XXX - Setup setjmp error handler if an error occurs anywhere deep during
|
||||||
|
* loading/saving execute a longjmp() and continue execution here */
|
||||||
|
if (setjmp(_sl.excpt)) {
|
||||||
|
AbortSaveLoad();
|
||||||
|
_sl.buf = tmp;
|
||||||
|
_sl.excpt_uninit();
|
||||||
|
|
||||||
|
ShowInfoF("Save game failed: %s.", _sl.excpt_msg);
|
||||||
|
ShowErrorMessage(STR_4007_GAME_SAVE_FAILED, STR_NULL, 0, 0);
|
||||||
|
|
||||||
|
SaveFileDone();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have written our stuff to memory, now write it to file! */
|
||||||
|
hdr[0] = fmt->tag;
|
||||||
|
hdr[1] = TO_BE32((SAVEGAME_MAJOR_VERSION << 16) + (SAVEGAME_MINOR_VERSION << 8));
|
||||||
|
if (fwrite(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError("file write failed");
|
||||||
|
|
||||||
|
if (!fmt->init_write()) SlError("cannot initialize compressor");
|
||||||
|
tmp = _sl.buf; // XXX - init_write can change _sl.buf, so update it
|
||||||
|
|
||||||
|
{
|
||||||
|
uint i;
|
||||||
|
uint count = 1 << _saveload_pool.block_size_bits;
|
||||||
|
|
||||||
|
assert(_save_byte_count == _sl.offs_base);
|
||||||
|
for (i = 0; i != _saveload_pool.current_blocks - 1; i++) {
|
||||||
|
_sl.buf = _saveload_pool.blocks[i];
|
||||||
|
fmt->writer(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The last block is (almost) always not fully filled, so only write away
|
||||||
|
* as much data as it is in there */
|
||||||
|
_sl.buf = _saveload_pool.blocks[i];
|
||||||
|
fmt->writer(_save_byte_count - (i * count));
|
||||||
|
|
||||||
|
_sl.buf = tmp; // XXX - reset _sl.buf to its original value to let it continue its internal usage
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt->uninit_write();
|
||||||
|
assert(_save_byte_count == _sl.offs_base);
|
||||||
|
GetSavegameFormat("memory")->uninit_write(); // clean the memorypool
|
||||||
|
fclose(_sl.fh);
|
||||||
|
|
||||||
|
SaveFileDone();
|
||||||
|
CloseOTTDThread();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main Save or Load function where the high-level saveload functions are
|
* Main Save or Load function where the high-level saveload functions are
|
||||||
* handled. It opens the savegame, selects format and checks versions
|
* handled. It opens the savegame, selects format and checks versions
|
||||||
|
@ -1133,6 +1262,12 @@ int SaveOrLoad(const char *filename, int mode)
|
||||||
return SL_OK;
|
return SL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* An instance of saving is already active, don't start any other cause of global variables */
|
||||||
|
if (_saving_game == true) {
|
||||||
|
if (!_do_autosave) ShowErrorMessage(_error_message, STR_SAVE_STILL_IN_PROGRESS, 0, 0);
|
||||||
|
return SL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
_sl.fh = fopen(filename, (mode == SL_SAVE) ? "wb" : "rb");
|
_sl.fh = fopen(filename, (mode == SL_SAVE) ? "wb" : "rb");
|
||||||
if (_sl.fh == NULL) {
|
if (_sl.fh == NULL) {
|
||||||
DEBUG(misc, 0) ("Cannot open savegame for saving/loading.");
|
DEBUG(misc, 0) ("Cannot open savegame for saving/loading.");
|
||||||
|
@ -1147,7 +1282,8 @@ int SaveOrLoad(const char *filename, int mode)
|
||||||
_sl.includes = _desc_includes;
|
_sl.includes = _desc_includes;
|
||||||
_sl.chs = _chunk_handlers;
|
_sl.chs = _chunk_handlers;
|
||||||
|
|
||||||
/* Setup setjmp error handler, if it fails don't even bother loading the game */
|
/* XXX - Setup setjmp error handler if an error occurs anywhere deep during
|
||||||
|
* loading/saving execute a longjmp() and continue execution here */
|
||||||
if (setjmp(_sl.excpt)) {
|
if (setjmp(_sl.excpt)) {
|
||||||
AbortSaveLoad();
|
AbortSaveLoad();
|
||||||
|
|
||||||
|
@ -1168,8 +1304,10 @@ int SaveOrLoad(const char *filename, int mode)
|
||||||
* be clobbered by `longjmp' or `vfork'" */
|
* be clobbered by `longjmp' or `vfork'" */
|
||||||
version = 0;
|
version = 0;
|
||||||
|
|
||||||
|
/* General tactic is to first save the game to memory, then use an available writer
|
||||||
|
* to write it to file, either in threaded mode if possible, or single-threaded */
|
||||||
if (mode == SL_SAVE) { /* SAVE game */
|
if (mode == SL_SAVE) { /* SAVE game */
|
||||||
fmt = GetSavegameFormat(_savegame_format);
|
fmt = GetSavegameFormat("memory"); // write to memory
|
||||||
|
|
||||||
_sl.write_bytes = fmt->writer;
|
_sl.write_bytes = fmt->writer;
|
||||||
_sl.excpt_uninit = fmt->uninit_write;
|
_sl.excpt_uninit = fmt->uninit_write;
|
||||||
|
@ -1178,20 +1316,23 @@ int SaveOrLoad(const char *filename, int mode)
|
||||||
return AbortSaveLoad();
|
return AbortSaveLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
hdr[0] = fmt->tag;
|
|
||||||
hdr[1] = TO_BE32((SAVEGAME_MAJOR_VERSION << 16) + (SAVEGAME_MINOR_VERSION << 8));
|
|
||||||
if (fwrite(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError("Writing savegame header failed");
|
|
||||||
|
|
||||||
_sl.version = SAVEGAME_MAJOR_VERSION;
|
_sl.version = SAVEGAME_MAJOR_VERSION;
|
||||||
|
|
||||||
BeforeSaveGame();
|
BeforeSaveGame();
|
||||||
SlSaveChunks();
|
SlSaveChunks();
|
||||||
SlWriteFill(); // flush the save buffer
|
SlWriteFill(); // flush the save buffer
|
||||||
fmt->uninit_write();
|
|
||||||
|
/* Write to file */
|
||||||
|
if (_network_server || !CreateOTTDThread(&SaveFileToDisk, NULL)) {
|
||||||
|
DEBUG(misc, 1) ("cannot create savegame thread, reverting to single-threaded mode...");
|
||||||
|
SaveFileToDisk(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
} else { /* LOAD game */
|
} else { /* LOAD game */
|
||||||
|
assert(mode == SL_LOAD);
|
||||||
|
|
||||||
if (fread(hdr, sizeof(hdr), 1, _sl.fh) != 1) {
|
if (fread(hdr, sizeof(hdr), 1, _sl.fh) != 1) {
|
||||||
DEBUG(misc, 0) ("Cannot read Savegame header, aborting.");
|
DEBUG(misc, 0) ("Cannot read savegame header, aborting.");
|
||||||
return AbortSaveLoad();
|
return AbortSaveLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1237,22 +1378,20 @@ int SaveOrLoad(const char *filename, int mode)
|
||||||
return AbortSaveLoad();
|
return AbortSaveLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX - ??? Set the current map to 256x256, in case of an old map.
|
/* Old maps were hardcoded to 256x256 and thus did not contain
|
||||||
* Else MAPS will read the wrong information. This should initialize
|
* any mapsize information. Pre-initialize to 256x256 to not to
|
||||||
* to savegame mapsize no?? */
|
* confuse old games */
|
||||||
InitializeGame(8, 8);
|
InitializeGame(8, 8);
|
||||||
|
|
||||||
SlLoadChunks();
|
SlLoadChunks();
|
||||||
fmt->uninit_read();
|
fmt->uninit_read();
|
||||||
|
fclose(_sl.fh);
|
||||||
|
|
||||||
|
/* After loading fix up savegame for any internal changes that
|
||||||
|
* might've occured since then. If it fails, load back the old game */
|
||||||
|
if (!AfterLoadGame(version)) return SL_REINIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(_sl.fh);
|
|
||||||
|
|
||||||
/* After loading fix up savegame for any internal changes that
|
|
||||||
* might've occured since then. If it fails, load back the old game */
|
|
||||||
if (mode == SL_LOAD && !AfterLoadGame(version))
|
|
||||||
return SL_REINIT;
|
|
||||||
|
|
||||||
return SL_OK;
|
return SL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
ttd.c
1
ttd.c
|
@ -686,6 +686,7 @@ int ttd_main(int argc, char* argv[])
|
||||||
|
|
||||||
while (_video_driver->main_loop() == ML_SWITCHDRIVER) {}
|
while (_video_driver->main_loop() == ML_SWITCHDRIVER) {}
|
||||||
|
|
||||||
|
JoinOTTDThread();
|
||||||
IConsoleFree();
|
IConsoleFree();
|
||||||
|
|
||||||
#ifdef ENABLE_NETWORK
|
#ifdef ENABLE_NETWORK
|
||||||
|
|
16
unix.c
16
unix.c
|
@ -11,6 +11,7 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) || defined(__GLIBC__)
|
#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) || defined(__GLIBC__)
|
||||||
#define HAS_STATVFS
|
#define HAS_STATVFS
|
||||||
|
@ -554,3 +555,18 @@ bool InsertTextBufferClipboard(Textbuf *tb)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static pthread_t thread1 = 0;
|
||||||
|
bool CreateOTTDThread(void *func, void *param)
|
||||||
|
{
|
||||||
|
return (pthread_create(&thread1, NULL, func, param) == 0) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseOTTDThread(void) {return;}
|
||||||
|
|
||||||
|
void JoinOTTDThread(void)
|
||||||
|
{
|
||||||
|
if (thread1 == 0) return;
|
||||||
|
|
||||||
|
pthread_join(thread1, NULL);
|
||||||
|
}
|
||||||
|
|
23
win32.c
23
win32.c
|
@ -2242,3 +2242,26 @@ bool InsertTextBufferClipboard(Textbuf *tb)
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static HANDLE hThread;
|
||||||
|
|
||||||
|
bool CreateOTTDThread(void *func, void *param)
|
||||||
|
{
|
||||||
|
DWORD dwThreadId;
|
||||||
|
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, param, 0, &dwThreadId);
|
||||||
|
SetThreadPriority(hThread, THREAD_PRIORITY_BELOW_NORMAL);
|
||||||
|
|
||||||
|
return (hThread == NULL) ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseOTTDThread(void)
|
||||||
|
{
|
||||||
|
if (!CloseHandle(hThread)) DEBUG(misc, 0) ("Failed to close thread?...");
|
||||||
|
}
|
||||||
|
|
||||||
|
void JoinOTTDThread(void)
|
||||||
|
{
|
||||||
|
if (hThread == NULL) return;
|
||||||
|
|
||||||
|
WaitForSingleObject(hThread, INFINITE);
|
||||||
|
}
|
Loading…
Reference in New Issue