Added a hint to control the Windows timer resolution: SDL_HINT_TIMER_RESOLUTION
Added an API to watch hint changes: SDL_AddHintCallback(), SDL_DelHintCallback() You can now dynamically set the joystick background event hint.
This commit is contained in:
parent
9102ff84ca
commit
6956070880
7 changed files with 212 additions and 89 deletions
|
@ -67,7 +67,6 @@
|
|||
04F7808512FB753F00FC43C0 /* SDL_nullframebuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 04F7808312FB753F00FC43C0 /* SDL_nullframebuffer.c */; };
|
||||
04FFAB8B12E23B8D00BA343D /* SDL_atomic.c in Sources */ = {isa = PBXBuildFile; fileRef = 04FFAB8912E23B8D00BA343D /* SDL_atomic.c */; };
|
||||
04FFAB8C12E23B8D00BA343D /* SDL_spinlock.c in Sources */ = {isa = PBXBuildFile; fileRef = 04FFAB8A12E23B8D00BA343D /* SDL_spinlock.c */; };
|
||||
22C905CD13A22646003FE4E4 /* SDL_hints_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 22C905CC13A22646003FE4E4 /* SDL_hints_c.h */; };
|
||||
56EA86FB13E9EC2B002E47EB /* SDL_coreaudio.c in Sources */ = {isa = PBXBuildFile; fileRef = 56EA86F913E9EC2B002E47EB /* SDL_coreaudio.c */; };
|
||||
56EA86FC13E9EC2B002E47EB /* SDL_coreaudio.h in Headers */ = {isa = PBXBuildFile; fileRef = 56EA86FA13E9EC2B002E47EB /* SDL_coreaudio.h */; };
|
||||
56ED04E1118A8EE200A56AA6 /* SDL_power.c in Sources */ = {isa = PBXBuildFile; fileRef = 56ED04E0118A8EE200A56AA6 /* SDL_power.c */; };
|
||||
|
@ -260,7 +259,6 @@
|
|||
04F7808312FB753F00FC43C0 /* SDL_nullframebuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_nullframebuffer.c; sourceTree = "<group>"; };
|
||||
04FFAB8912E23B8D00BA343D /* SDL_atomic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_atomic.c; sourceTree = "<group>"; };
|
||||
04FFAB8A12E23B8D00BA343D /* SDL_spinlock.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_spinlock.c; sourceTree = "<group>"; };
|
||||
22C905CC13A22646003FE4E4 /* SDL_hints_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_hints_c.h; path = ../../src/SDL_hints_c.h; sourceTree = "<group>"; };
|
||||
56EA86F913E9EC2B002E47EB /* SDL_coreaudio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_coreaudio.c; path = coreaudio/SDL_coreaudio.c; sourceTree = "<group>"; };
|
||||
56EA86FA13E9EC2B002E47EB /* SDL_coreaudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_coreaudio.h; path = coreaudio/SDL_coreaudio.h; sourceTree = "<group>"; };
|
||||
56ED04E0118A8EE200A56AA6 /* SDL_power.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_power.c; path = ../../src/power/SDL_power.c; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -747,7 +745,6 @@
|
|||
FD99B9D60DD52EDC00FB1D6B /* SDL_fatal.c */,
|
||||
FD99B9D70DD52EDC00FB1D6B /* SDL_fatal.h */,
|
||||
0442EC5412FE1C3F004C9285 /* SDL_hints.c */,
|
||||
22C905CC13A22646003FE4E4 /* SDL_hints_c.h */,
|
||||
04BAC09B1300C1290055DE28 /* SDL_log.c */,
|
||||
FD99B9D80DD52EDC00FB1D6B /* SDL.c */,
|
||||
);
|
||||
|
@ -967,7 +964,6 @@
|
|||
0442EC5012FE1C1E004C9285 /* SDL_render_sw_c.h in Headers */,
|
||||
0402A85A12FE70C600CECEE3 /* SDL_shaders_gles2.h in Headers */,
|
||||
04BAC09C1300C1290055DE28 /* SDL_assert_c.h in Headers */,
|
||||
22C905CD13A22646003FE4E4 /* SDL_hints_c.h in Headers */,
|
||||
56EA86FC13E9EC2B002E47EB /* SDL_coreaudio.h in Headers */,
|
||||
93CB792313FC5E5200BD3E05 /* SDL_uikitviewcontroller.h in Headers */,
|
||||
AA628ADC159369E3005138DD /* SDL_rotate.h in Headers */,
|
||||
|
|
|
@ -218,13 +218,13 @@ extern "C" {
|
|||
/**
|
||||
* \brief A variable that lets you enable joystick (and gamecontroller) events even when your app is in the background.
|
||||
*
|
||||
* The default value is "0".
|
||||
*
|
||||
* The variable can be set to the following values:
|
||||
* "0" - Disable joystick & gamecontroller input events when the
|
||||
* application is in the background.
|
||||
* "1" - Enable joystick & gamecontroller input events when the
|
||||
* application is in the backgroumd.
|
||||
*
|
||||
* The default value is "0". This hint may be set at any time.
|
||||
*/
|
||||
#define SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS "SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS"
|
||||
|
||||
|
@ -240,6 +240,23 @@ extern "C" {
|
|||
#define SDL_HINT_ALLOW_TOPMOST "SDL_ALLOW_TOPMOST"
|
||||
|
||||
|
||||
/**
|
||||
* \brief A variable that controls the timer resolution, in milliseconds.
|
||||
*
|
||||
* The higher resolution the timer, the more frequently the CPU services
|
||||
* timer interrupts, and the more precise delays are, but this takes up
|
||||
* power and CPU time. This hint is only used on Windows 7 and earlier.
|
||||
*
|
||||
* See this blog post for more information:
|
||||
* http://randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted/
|
||||
*
|
||||
* If this variable is set to "0", the system timer resolution is not set.
|
||||
*
|
||||
* The default value is "1". This hint may be set at any time.
|
||||
*/
|
||||
#define SDL_HINT_TIMER_RESOLUTION "SDL_TIMER_RESOLUTION"
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \brief An enumeration of hint priorities
|
||||
|
@ -273,7 +290,6 @@ extern DECLSPEC SDL_bool SDLCALL SDL_SetHintWithPriority(const char *name,
|
|||
extern DECLSPEC SDL_bool SDLCALL SDL_SetHint(const char *name,
|
||||
const char *value);
|
||||
|
||||
|
||||
/**
|
||||
* \brief Get a hint
|
||||
*
|
||||
|
@ -281,6 +297,29 @@ extern DECLSPEC SDL_bool SDLCALL SDL_SetHint(const char *name,
|
|||
*/
|
||||
extern DECLSPEC const char * SDLCALL SDL_GetHint(const char *name);
|
||||
|
||||
/**
|
||||
* \brief Add a function to watch a particular hint
|
||||
*
|
||||
* \param name The hint to watch
|
||||
* \param callback The function to call when the hint value changes
|
||||
* \param userdata A pointer to pass to the callback function
|
||||
*/
|
||||
typedef void (*SDL_HintCallback)(void *userdata, const char *name, const char *oldValue, const char *newValue);
|
||||
extern DECLSPEC void SDLCALL SDL_AddHintCallback(const char *name,
|
||||
SDL_HintCallback callback,
|
||||
void *userdata);
|
||||
|
||||
/**
|
||||
* \brief Remove a function watching a particular hint
|
||||
*
|
||||
* \param name The hint being watched
|
||||
* \param callback The function being called when the hint value changes
|
||||
* \param userdata A pointer being passed to the callback function
|
||||
*/
|
||||
extern DECLSPEC void SDLCALL SDL_DelHintCallback(const char *name,
|
||||
SDL_HintCallback callback,
|
||||
void *userdata);
|
||||
|
||||
/**
|
||||
* \brief Clear all hints
|
||||
*
|
||||
|
|
137
src/SDL_hints.c
137
src/SDL_hints.c
|
@ -21,43 +21,35 @@
|
|||
#include "SDL_config.h"
|
||||
|
||||
#include "SDL_hints.h"
|
||||
#include "SDL_hints_c.h"
|
||||
#include "SDL_error.h"
|
||||
|
||||
|
||||
/* Assuming there aren't many hints set and they aren't being queried in
|
||||
critical performance paths, we'll just use a linked list here.
|
||||
critical performance paths, we'll just use linked lists here.
|
||||
*/
|
||||
typedef struct SDL_HintWatch {
|
||||
SDL_HintCallback callback;
|
||||
void *userdata;
|
||||
struct SDL_HintWatch *next;
|
||||
} SDL_HintWatch;
|
||||
|
||||
typedef struct SDL_Hint {
|
||||
char *name;
|
||||
char *value;
|
||||
SDL_HintPriority priority;
|
||||
SDL_HintChangedCb callback;
|
||||
SDL_HintWatch *callbacks;
|
||||
struct SDL_Hint *next;
|
||||
} SDL_Hint;
|
||||
|
||||
static SDL_Hint *SDL_hints;
|
||||
|
||||
SDL_bool
|
||||
SDL_RegisterHintChangedCb(const char *name, SDL_HintChangedCb hintCb)
|
||||
{
|
||||
SDL_Hint *hint;
|
||||
|
||||
for (hint = SDL_hints; hint; hint = hint->next) {
|
||||
if (SDL_strcmp(name, hint->name) == 0) {
|
||||
hint->callback = hintCb;
|
||||
return SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
SDL_bool
|
||||
SDL_SetHintWithPriority(const char *name, const char *value,
|
||||
SDL_HintPriority priority)
|
||||
{
|
||||
const char *env;
|
||||
SDL_Hint *hint;
|
||||
SDL_HintWatch *entry;
|
||||
|
||||
if (!name || !value) {
|
||||
return SDL_FALSE;
|
||||
|
@ -73,12 +65,21 @@ SDL_SetHintWithPriority(const char *name, const char *value,
|
|||
if (priority < hint->priority) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
if (SDL_strcmp(hint->value, value) != 0) {
|
||||
if (hint->callback != NULL) {
|
||||
(*hint->callback)(name, hint->value, value);
|
||||
if (!hint->value || !value || SDL_strcmp(hint->value, value) != 0) {
|
||||
for (entry = hint->callbacks; entry; ) {
|
||||
/* Save the next entry in case this one is deleted */
|
||||
SDL_HintWatch *next = entry->next;
|
||||
entry->callback(entry->userdata, name, hint->value, value);
|
||||
entry = next;
|
||||
}
|
||||
if (hint->value) {
|
||||
SDL_free(hint->value);
|
||||
}
|
||||
if (value) {
|
||||
hint->value = SDL_strdup(value);
|
||||
} else {
|
||||
hint->value = NULL;
|
||||
}
|
||||
SDL_free(hint->value);
|
||||
hint->value = SDL_strdup(value);
|
||||
}
|
||||
hint->priority = priority;
|
||||
return SDL_TRUE;
|
||||
|
@ -91,9 +92,9 @@ SDL_SetHintWithPriority(const char *name, const char *value,
|
|||
return SDL_FALSE;
|
||||
}
|
||||
hint->name = SDL_strdup(name);
|
||||
hint->value = SDL_strdup(value);
|
||||
hint->value = value ? SDL_strdup(value) : NULL;
|
||||
hint->priority = priority;
|
||||
hint->callback = NULL;
|
||||
hint->callbacks = NULL;
|
||||
hint->next = SDL_hints;
|
||||
SDL_hints = hint;
|
||||
return SDL_TRUE;
|
||||
|
@ -123,16 +124,100 @@ SDL_GetHint(const char *name)
|
|||
return env;
|
||||
}
|
||||
|
||||
void
|
||||
SDL_AddHintCallback(const char *name, SDL_HintCallback callback, void *userdata)
|
||||
{
|
||||
SDL_Hint *hint;
|
||||
SDL_HintWatch *entry;
|
||||
const char *value;
|
||||
|
||||
if (!name || !*name) {
|
||||
SDL_InvalidParamError("name");
|
||||
return;
|
||||
}
|
||||
if (!callback) {
|
||||
SDL_InvalidParamError("callback");
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_DelHintCallback(name, callback, userdata);
|
||||
|
||||
entry = (SDL_HintWatch *)SDL_malloc(sizeof(*entry));
|
||||
entry->callback = callback;
|
||||
entry->userdata = userdata;
|
||||
|
||||
for (hint = SDL_hints; hint; hint = hint->next) {
|
||||
if (SDL_strcmp(name, hint->name) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hint) {
|
||||
/* Need to add a hint entry for this watcher */
|
||||
hint = (SDL_Hint *)SDL_malloc(sizeof(*hint));
|
||||
if (!hint) {
|
||||
return;
|
||||
}
|
||||
hint->name = SDL_strdup(name);
|
||||
hint->value = NULL;
|
||||
hint->priority = SDL_HINT_DEFAULT;
|
||||
hint->callbacks = NULL;
|
||||
hint->next = SDL_hints;
|
||||
SDL_hints = hint;
|
||||
}
|
||||
|
||||
/* Add it to the callbacks for this hint */
|
||||
entry->next = hint->callbacks;
|
||||
hint->callbacks = entry;
|
||||
|
||||
/* Now call it with the current value */
|
||||
value = SDL_GetHint(name);
|
||||
callback(userdata, name, value, value);
|
||||
}
|
||||
|
||||
void
|
||||
SDL_DelHintCallback(const char *name, SDL_HintCallback callback, void *userdata)
|
||||
{
|
||||
SDL_Hint *hint;
|
||||
SDL_HintWatch *entry, *prev;
|
||||
|
||||
for (hint = SDL_hints; hint; hint = hint->next) {
|
||||
if (SDL_strcmp(name, hint->name) == 0) {
|
||||
prev = NULL;
|
||||
for (entry = hint->callbacks; entry; entry = entry->next) {
|
||||
if (callback == entry->callback && userdata == entry->userdata) {
|
||||
if (prev) {
|
||||
prev->next = entry->next;
|
||||
} else {
|
||||
hint->callbacks = entry->next;
|
||||
}
|
||||
SDL_free(entry);
|
||||
break;
|
||||
}
|
||||
prev = entry;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_ClearHints(void)
|
||||
{
|
||||
SDL_Hint *hint;
|
||||
SDL_HintWatch *entry;
|
||||
|
||||
while (SDL_hints) {
|
||||
hint = SDL_hints;
|
||||
SDL_hints = hint->next;
|
||||
|
||||
SDL_free(hint->name);
|
||||
SDL_free(hint->value);
|
||||
if (hint->value) {
|
||||
SDL_free(hint->value);
|
||||
}
|
||||
for (entry = hint->callbacks; entry; ) {
|
||||
SDL_HintWatch *freeable = entry;
|
||||
entry = entry->next;
|
||||
SDL_free(freeable);
|
||||
}
|
||||
SDL_free(hint);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef _SDL_hints_c_h
|
||||
#define _SDL_hints_c_h
|
||||
|
||||
/**
|
||||
* \brief A callback function that is optionally called when a hint changes
|
||||
*/
|
||||
typedef void (*SDL_HintChangedCb)(const char *name, const char *oldValue, const char *newValue);
|
||||
|
||||
extern SDL_bool SDL_RegisterHintChangedCb(const char *name, SDL_HintChangedCb hintCb);
|
||||
|
||||
#endif /* _SDL_hints_c_h */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
|
@ -36,17 +36,24 @@ static SDL_bool SDL_joystick_allows_background_events = SDL_FALSE;
|
|||
static SDL_Joystick *SDL_joysticks = NULL;
|
||||
static SDL_Joystick *SDL_updating_joystick = NULL;
|
||||
|
||||
static void
|
||||
SDL_JoystickAllowBackgroundEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
|
||||
{
|
||||
if (hint && *hint == '1') {
|
||||
SDL_joystick_allows_background_events = SDL_TRUE;
|
||||
} else {
|
||||
SDL_joystick_allows_background_events = SDL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
SDL_JoystickInit(void)
|
||||
{
|
||||
const char *hint;
|
||||
int status;
|
||||
|
||||
/* Check to see if we should allow joystick events while in the background */
|
||||
hint = SDL_GetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS);
|
||||
if (hint && *hint == '1') {
|
||||
SDL_joystick_allows_background_events = SDL_TRUE;
|
||||
}
|
||||
/* See if we should allow joystick events while in the background */
|
||||
SDL_AddHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
|
||||
SDL_JoystickAllowBackgroundEventsChanged, NULL);
|
||||
|
||||
#if !SDL_EVENTS_DISABLED
|
||||
if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0) {
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
#include <mmsystem.h>
|
||||
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_hints.h"
|
||||
|
||||
#define TIME_WRAP_VALUE (~(DWORD)0)
|
||||
|
||||
/* The first (low-resolution) ticks value of the application */
|
||||
static DWORD start;
|
||||
|
@ -41,6 +41,40 @@ static LARGE_INTEGER hires_start_ticks;
|
|||
static LARGE_INTEGER hires_ticks_per_second;
|
||||
#endif
|
||||
|
||||
static void
|
||||
timeSetPeriod(UINT uPeriod)
|
||||
{
|
||||
static UINT timer_period = 0;
|
||||
|
||||
if (uPeriod != timer_period) {
|
||||
if (timer_period) {
|
||||
timeEndPeriod(timer_period);
|
||||
}
|
||||
|
||||
timer_period = uPeriod;
|
||||
|
||||
if (timer_period) {
|
||||
timeBeginPeriod(timer_period);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SDL_TimerResolutionChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
|
||||
{
|
||||
UINT uPeriod;
|
||||
|
||||
/* Unless the hint says otherwise, let's have good sleep precision */
|
||||
if (hint && *hint) {
|
||||
uPeriod = SDL_atoi(hint);
|
||||
} else {
|
||||
uPeriod = 1;
|
||||
}
|
||||
if (uPeriod || oldValue != hint) {
|
||||
timeSetPeriod(uPeriod);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SDL_StartTicks(void)
|
||||
{
|
||||
|
@ -56,16 +90,19 @@ SDL_StartTicks(void)
|
|||
QueryPerformanceCounter(&hires_start_ticks);
|
||||
} else {
|
||||
hires_timer_available = FALSE;
|
||||
timeBeginPeriod(1); /* use 1 ms timer precision */
|
||||
timeSetPeriod(1); /* use 1 ms timer precision */
|
||||
start = timeGetTime();
|
||||
}
|
||||
#endif
|
||||
|
||||
SDL_AddHintCallback(SDL_HINT_TIMER_RESOLUTION,
|
||||
SDL_TimerResolutionChanged, NULL);
|
||||
}
|
||||
|
||||
Uint32
|
||||
SDL_GetTicks(void)
|
||||
{
|
||||
DWORD now, ticks;
|
||||
DWORD now;
|
||||
#ifndef USE_GETTICKCOUNT
|
||||
LARGE_INTEGER hires_now;
|
||||
#endif
|
||||
|
@ -86,12 +123,7 @@ SDL_GetTicks(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (now < start) {
|
||||
ticks = (TIME_WRAP_VALUE - start) + now;
|
||||
} else {
|
||||
ticks = (now - start);
|
||||
}
|
||||
return (ticks);
|
||||
return (now - start);
|
||||
}
|
||||
|
||||
Uint64
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include "../SDL_sysvideo.h"
|
||||
#include "SDL_assert.h"
|
||||
#include "SDL_hints.h"
|
||||
#include "../../SDL_hints_c.h"
|
||||
#include "SDL_system.h"
|
||||
#include "SDL_main.h"
|
||||
|
||||
|
@ -69,11 +68,10 @@ int main(int argc, char **argv)
|
|||
return exit_status;
|
||||
}
|
||||
|
||||
static void SDL_IdleTimerDisabledChanged(const char *name, const char *oldValue, const char *newValue)
|
||||
static void
|
||||
SDL_IdleTimerDisabledChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
|
||||
{
|
||||
SDL_assert(SDL_strcmp(name, SDL_HINT_IDLE_TIMER_DISABLED) == 0);
|
||||
|
||||
BOOL disable = (*newValue != '0');
|
||||
BOOL disable = (hint && *hint != '0');
|
||||
[UIApplication sharedApplication].idleTimerDisabled = disable;
|
||||
}
|
||||
|
||||
|
@ -218,8 +216,8 @@ static void SDL_IdleTimerDisabledChanged(const char *name, const char *oldValue,
|
|||
[[NSFileManager defaultManager] changeCurrentDirectoryPath: [[NSBundle mainBundle] resourcePath]];
|
||||
|
||||
/* register a callback for the idletimer hint */
|
||||
SDL_SetHint(SDL_HINT_IDLE_TIMER_DISABLED, "0");
|
||||
SDL_RegisterHintChangedCb(SDL_HINT_IDLE_TIMER_DISABLED, &SDL_IdleTimerDisabledChanged);
|
||||
SDL_AddHintCallback(SDL_HINT_IDLE_TIMER_DISABLED,
|
||||
SDL_IdleTimerDisabledChanged, NULL);
|
||||
|
||||
SDL_SetMainReady();
|
||||
[self performSelector:@selector(postFinishLaunch) withObject:nil afterDelay:0.0];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue