osx toplevel splash windows

This commit is contained in:
David Rose 2009-08-22 21:26:25 +00:00
parent 78039485d6
commit 29a65d32a9
9 changed files with 279 additions and 59 deletions

View File

@ -76,6 +76,7 @@ P3DInstance(P3D_request_ready_func *func,
_panda3d = NULL; _panda3d = NULL;
_splash_window = NULL; _splash_window = NULL;
_instance_window_opened = false; _instance_window_opened = false;
_stuff_to_download = false;
INIT_LOCK(_request_lock); INIT_LOCK(_request_lock);
_requested_stop = false; _requested_stop = false;
@ -179,10 +180,13 @@ P3DInstance::
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void P3DInstance:: void P3DInstance::
set_p3d_url(const string &p3d_url) { set_p3d_url(const string &p3d_url) {
nout << "set_p3d_url(" << p3d_url << ")\n";
// Make a temporary file to receive the instance data. // Make a temporary file to receive the instance data.
assert(_temp_p3d_filename == NULL); assert(_temp_p3d_filename == NULL);
_temp_p3d_filename = new P3DTemporaryFile(".p3d"); _temp_p3d_filename = new P3DTemporaryFile(".p3d");
_stuff_to_download = true;
// Maybe it's time to open a splash window now.
make_splash_window();
// Mark the time we started downloading, so we'll know when to set // Mark the time we started downloading, so we'll know when to set
// the install label. // the install label.
@ -210,7 +214,6 @@ set_p3d_url(const string &p3d_url) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void P3DInstance:: void P3DInstance::
set_p3d_filename(const string &p3d_filename) { set_p3d_filename(const string &p3d_filename) {
nout << "set_p3d_filename(" << p3d_filename << ")\n";
_got_fparams = true; _got_fparams = true;
_fparams.set_p3d_filename(p3d_filename); _fparams.set_p3d_filename(p3d_filename);
@ -268,11 +271,10 @@ set_wparams(const P3DWindowParams &wparams) {
if (_wparams.get_window_type() != P3D_WT_hidden) { if (_wparams.get_window_type() != P3D_WT_hidden) {
// Update or create the splash window. // Update or create the splash window.
if (!_instance_window_opened) { if (_splash_window != NULL) {
if (_splash_window == NULL) {
make_splash_window();
}
_splash_window->set_wparams(_wparams); _splash_window->set_wparams(_wparams);
} else {
make_splash_window();
} }
#ifdef __APPLE__ #ifdef __APPLE__
@ -281,7 +283,6 @@ set_wparams(const P3DWindowParams &wparams) {
// to the browser. Set up this mechanism. // to the browser. Set up this mechanism.
int x_size = _wparams.get_win_width(); int x_size = _wparams.get_win_width();
int y_size = _wparams.get_win_height(); int y_size = _wparams.get_win_height();
nout << "x_size, y_size = " << x_size << ", " << y_size << "\n";
if (x_size != 0 && y_size != 0) { if (x_size != 0 && y_size != 0) {
if (_swbuffer == NULL || _swbuffer->get_x_size() != x_size || if (_swbuffer == NULL || _swbuffer->get_x_size() != x_size ||
_swbuffer->get_y_size() != y_size) { _swbuffer->get_y_size() != y_size) {
@ -1169,25 +1170,38 @@ handle_script_request(const string &operation, P3D_object *object,
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: P3DInstance::make_splash_window // Function: P3DInstance::make_splash_window
// Access: Private // Access: Private
// Description: Creates the splash window to be displayed at startup. // Description: Creates the splash window to be displayed at startup,
// This method is called as soon as we have received // if it's time.
// both _fparams and _wparams.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void P3DInstance:: void P3DInstance::
make_splash_window() { make_splash_window() {
assert(_splash_window == NULL); if (_splash_window != NULL || _instance_window_opened) {
// Already got one, or we're already showing the real instance.
return;
}
if (!_got_wparams) {
// Don't know where to put it yet.
return;
}
if (_wparams.get_window_type() == P3D_WT_toplevel && !_stuff_to_download) {
// If it's a toplevel window, then we don't want a splash window
// until we have stuff to download.
return;
}
_splash_window = new SplashWindowType(this); _splash_window = new SplashWindowType(this);
_splash_window->set_wparams(_wparams);
_splash_window->set_install_label(_install_label); _splash_window->set_install_label(_install_label);
string splash_image_url = _fparams.lookup_token("splash_img"); string splash_image_url = _fparams.lookup_token("splash_img");
if (!_fparams.has_token("splash_img")) { if (!_fparams.has_token("splash_img")) {
// No specific splash image is specified; get the default splash // No specific splash image is specified; get the default splash
// image. // image.
if (_panda3d != NULL) { splash_image_url = PANDA_PACKAGE_HOST_URL;
splash_image_url = _panda3d->get_host()->get_host_url_prefix(); if (!splash_image_url.empty() && splash_image_url[splash_image_url.size() - 1] != '/') {
splash_image_url += "coreapi/splash.jpg"; splash_image_url += "/";
} }
splash_image_url += "coreapi/splash.jpg";
} }
if (splash_image_url.empty()) { if (splash_image_url.empty()) {
@ -1236,6 +1250,13 @@ report_package_info_ready(P3DPackage *package) {
<< " packages, total " << _total_download_size << " packages, total " << _total_download_size
<< " bytes required.\n"; << " bytes required.\n";
if (_downloading_packages.size() > 0) {
_stuff_to_download = true;
// Maybe it's time to open a splash window now.
make_splash_window();
}
if (_splash_window != NULL) { if (_splash_window != NULL) {
_splash_window->set_install_progress(0.0); _splash_window->set_install_progress(0.0);
} }
@ -1525,7 +1546,7 @@ paint_window() {
// offscreen, the top left of the clipping rectangle will no longer // offscreen, the top left of the clipping rectangle will no longer
// correspond to the top left of the original image. // correspond to the top left of the original image.
CGRect rect = CGContextGetClipBoundingBox(context); CGRect rect = CGContextGetClipBoundingBox(context);
cerr << "rect: " << rect.origin.x << " " << rect.origin.y nout << "rect: " << rect.origin.x << " " << rect.origin.y
<< " " << rect.size.width << " " << rect.size.height << "\n"; << " " << rect.size.width << " " << rect.size.height << "\n";
rect.size.width = x_size; rect.size.width = x_size;
rect.size.height = y_size; rect.size.height = y_size;

View File

@ -162,7 +162,6 @@ private:
bool _full_disk_access; bool _full_disk_access;
bool _hidden; bool _hidden;
// Not ref-counted: session is the parent.
P3DSession *_session; P3DSession *_session;
#ifdef __APPLE__ #ifdef __APPLE__
@ -182,6 +181,7 @@ private:
P3DSplashWindow *_splash_window; P3DSplashWindow *_splash_window;
string _install_label; string _install_label;
bool _instance_window_opened; bool _instance_window_opened;
bool _stuff_to_download;
// Members for deciding whether and when to display the progress bar // Members for deciding whether and when to display the progress bar
// for downloading the initial instance data. // for downloading the initial instance data.

View File

@ -31,6 +31,7 @@ P3DOsxSplashWindow(P3DInstance *inst) :
_image_data = NULL; _image_data = NULL;
_install_progress = 0; _install_progress = 0;
_got_wparams = false; _got_wparams = false;
_toplevel_window = NULL;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -40,6 +41,13 @@ P3DOsxSplashWindow(P3DInstance *inst) :
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
P3DOsxSplashWindow:: P3DOsxSplashWindow::
~P3DOsxSplashWindow() { ~P3DOsxSplashWindow() {
if (_toplevel_window != NULL) {
SetWRefCon(_toplevel_window, 0);
HideWindow(_toplevel_window);
DisposeWindow(_toplevel_window);
_toplevel_window = NULL;
}
if (_image != NULL) { if (_image != NULL) {
DisposeGWorld(_image); DisposeGWorld(_image);
} }
@ -61,6 +69,43 @@ void P3DOsxSplashWindow::
set_wparams(const P3DWindowParams &wparams) { set_wparams(const P3DWindowParams &wparams) {
P3DSplashWindow::set_wparams(wparams); P3DSplashWindow::set_wparams(wparams);
_got_wparams = true; _got_wparams = true;
if (_wparams.get_window_type() == P3D_WT_toplevel ||
_wparams.get_window_type() == P3D_WT_fullscreen) {
// Creating a toplevel splash window.
if (_toplevel_window == NULL) {
Rect r;
r.top = _wparams.get_win_y();
r.left = _wparams.get_win_x();
if (r.top == 0 && r.left == 0) {
r.top = 250;
r.left = 210;
}
r.right = r.left + _wparams.get_win_width();
r.bottom = r.top + _wparams.get_win_height();
WindowAttributes attrib =
kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute;
CreateNewWindow(kDocumentWindowClass, attrib, &r, &_toplevel_window);
EventHandlerRef application_event_ref_ref1;
EventTypeSpec list1[] = {
{ kEventClassWindow, kEventWindowDrawContent },
//{ kEventClassWindow, kEventWindowUpdate },
};
EventHandlerUPP gEvtHandler = NewEventHandlerUPP(st_event_callback);
InstallWindowEventHandler(_toplevel_window, gEvtHandler,
GetEventTypeCount(list1), list1, this, &application_event_ref_ref1);
ProcessSerialNumber psn = { 0, kCurrentProcess };
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
SetFrontProcess(&psn);
ShowWindow(_toplevel_window);
}
}
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -130,7 +175,7 @@ set_image_filename(const string &image_filename,
return; return;
} }
_inst->request_refresh(); refresh();
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -142,7 +187,7 @@ set_image_filename(const string &image_filename,
void P3DOsxSplashWindow:: void P3DOsxSplashWindow::
set_install_label(const string &install_label) { set_install_label(const string &install_label) {
_install_label = install_label; _install_label = install_label;
_inst->request_refresh(); refresh();
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -154,7 +199,7 @@ void P3DOsxSplashWindow::
set_install_progress(double install_progress) { set_install_progress(double install_progress) {
if ((int)(install_progress * 500.0) != (int)(_install_progress * 500.0)) { if ((int)(install_progress * 500.0) != (int)(_install_progress * 500.0)) {
// Only request a refresh if we're changing substantially. // Only request a refresh if we're changing substantially.
_inst->request_refresh(); refresh();
} }
_install_progress = install_progress; _install_progress = install_progress;
} }
@ -175,6 +220,25 @@ handle_event(P3D_event_data event) {
return false; return false;
} }
////////////////////////////////////////////////////////////////////
// Function: P3DOsxSplashWindow::refresh
// Access: Private
// Description: Requests that the window will be repainted.
////////////////////////////////////////////////////////////////////
void P3DOsxSplashWindow::
refresh() {
if (_toplevel_window != NULL) {
int win_width = _wparams.get_win_width();
int win_height = _wparams.get_win_height();
Rect r = { 0, 0, win_height, win_width };
InvalWindowRect(_toplevel_window, &r);
} else {
_inst->request_refresh();
}
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: P3DOsxSplashWindow::paint_window // Function: P3DOsxSplashWindow::paint_window
// Access: Private // Access: Private
@ -186,16 +250,28 @@ paint_window() {
return; return;
} }
GrafPtr out_port = _wparams.get_parent_window()._port; GrafPtr out_port = NULL;
GrafPtr portSave = NULL; GrafPtr portSave = NULL;
Boolean portChanged = QDSwapPort(out_port, &portSave); Boolean portChanged = false;
if (_toplevel_window != NULL) {
/*
GetPort(&portSave);
SetPortWindowPort(_toplevel_window);
BeginUpdate(_toplevel_window);
*/
GetPort(&out_port);
} else {
out_port = _wparams.get_parent_window()._port;
portChanged = QDSwapPort(out_port, &portSave);
}
int win_width = _wparams.get_win_width(); int win_width = _wparams.get_win_width();
int win_height = _wparams.get_win_height(); int win_height = _wparams.get_win_height();
Rect r = { 0, 0, win_height, win_width }; Rect r = { 0, 0, win_height, win_width };
ClipRect(&r); ClipRect(&r);
EraseRect(&r); EraseRect(&r);
if (_image != NULL) { if (_image != NULL) {
@ -246,8 +322,8 @@ paint_window() {
get_bar_placement(win_width, win_height, get_bar_placement(win_width, win_height,
bar_x, bar_y, bar_width, bar_height); bar_x, bar_y, bar_width, bar_height);
int progress_width = (int)((bar_width - 2) * _install_progress); if (_install_progress != 0.0) {
if (progress_width > 0) { int progress_width = (int)((bar_width - 2) * _install_progress);
int progress = bar_x + 1 + progress_width; int progress = bar_x + 1 + progress_width;
Rect rbar = { bar_y, bar_x, bar_y + bar_height, bar_x + bar_width }; Rect rbar = { bar_y, bar_x, bar_y + bar_height, bar_x + bar_width };
@ -262,37 +338,77 @@ paint_window() {
RGBColor black = { 0, 0, 0 }; RGBColor black = { 0, 0, 0 };
RGBForeColor(&black); RGBForeColor(&black);
if (!_install_label.empty()) {
// Now draw the install_label right above it.
TextFont(0);
TextFace(bold);
TextMode(srcOr);
TextSize(0);
Point numer = { 1, 1 };
Point denom = { 1, 1 };
FontInfo font_info;
StdTxMeas(_install_label.size(), _install_label.data(), &numer, &denom, &font_info);
int ascent = font_info.ascent * numer.v / denom.v;
int descent = font_info.descent * numer.v / denom.v;
int text_width = TextWidth(_install_label.data(), 0, _install_label.size());
int text_x = (win_width - text_width) / 2;
int text_y = bar_y - descent - 8;
Rect rtext = { text_y - ascent - 2, text_x - 2,
text_y + descent + 2, text_x + text_width + 2 };
EraseRect(&rtext);
MoveTo(text_x, text_y);
DrawText(_install_label.data(), 0, _install_label.size());
}
} }
if (!_install_label.empty()) { if (_toplevel_window == NULL) {
// Now draw the install_label right above it. if (portChanged) {
TextFont(0); QDSwapPort(portSave, NULL);
TextFace(bold); }
TextMode(srcOr); }
TextSize(0); }
Point numer = { 1, 1 }; ////////////////////////////////////////////////////////////////////
Point denom = { 1, 1 }; // Function: P3DOsxSplashWindow::st_event_callback
FontInfo font_info; // Access: Private, Static
StdTxMeas(_install_label.size(), _install_label.data(), &numer, &denom, &font_info); // Description: The event callback on the toplevel window.
int ascent = font_info.ascent * numer.v / denom.v; ////////////////////////////////////////////////////////////////////
int descent = font_info.descent * numer.v / denom.v; pascal OSStatus P3DOsxSplashWindow::
st_event_callback(EventHandlerCallRef my_handler, EventRef event,
void *user_data) {
return ((P3DOsxSplashWindow *)user_data)->event_callback(my_handler, event);
}
int text_width = TextWidth(_install_label.data(), 0, _install_label.size()); ////////////////////////////////////////////////////////////////////
int text_x = (win_width - text_width) / 2; // Function: P3DOsxSplashWindow::event_callback
int text_y = bar_y - descent - 8; // Access: Private
// Description: The event callback on the toplevel window.
////////////////////////////////////////////////////////////////////
OSStatus P3DOsxSplashWindow::
event_callback(EventHandlerCallRef my_handler, EventRef event) {
OSStatus result = eventNotHandledErr;
Rect rtext = { text_y - ascent - 2, text_x - 2, WindowRef window = NULL;
text_y + descent + 2, text_x + text_width + 2 }; UInt32 the_class = GetEventClass(event);
EraseRect(&rtext); UInt32 kind = GetEventKind(event);
MoveTo(text_x, text_y); GetEventParameter(event, kEventParamWindowRef, typeWindowRef, NULL,
DrawText(_install_label.data(), 0, _install_label.size()); sizeof(WindowRef), NULL, (void*) &window);
switch (the_class) {
case kEventClassWindow:
switch (kind) {
case kEventWindowDrawContent:
paint_window();
result = noErr;
}
} }
if (portChanged) { return result;
QDSwapPort(portSave, NULL);
}
} }

View File

@ -42,8 +42,14 @@ public:
virtual bool handle_event(P3D_event_data event); virtual bool handle_event(P3D_event_data event);
private: private:
void refresh();
void paint_window(); void paint_window();
static pascal OSStatus
st_event_callback(EventHandlerCallRef my_handler, EventRef event,
void *user_data);
OSStatus event_callback(EventHandlerCallRef my_handler, EventRef event);
private: private:
bool _got_wparams; bool _got_wparams;
GWorldPtr _image; GWorldPtr _image;
@ -52,6 +58,8 @@ private:
string _install_label; string _install_label;
double _install_progress; double _install_progress;
WindowRef _toplevel_window;
}; };
#include "p3dOsxSplashWindow.I" #include "p3dOsxSplashWindow.I"

View File

@ -81,7 +81,11 @@ P3DPackage::
_active_download = NULL; _active_download = NULL;
} }
assert(_temp_contents_file == NULL); if (_temp_contents_file != NULL) {
delete _temp_contents_file;
_temp_contents_file = NULL;
}
assert(_instances.empty()); assert(_instances.empty());
} }

View File

@ -255,9 +255,12 @@ write_stream(NPStream *stream, int offset, int len, void *buffer) {
return len; return len;
case PPDownloadRequest::RT_instance_data: case PPDownloadRequest::RT_instance_data:
// Here's a stream we don't really want. // Here's a stream we don't really want. But stopping it early
browser->destroystream(_npp_instance, stream, NPRES_USER_BREAK); // seems to freak out Safari. (And stopping it before it starts
return 0; // freaks out Firefox.)
// Whatever. We'll just quietly ignore the data.
return len;
default: default:
nout << "Unexpected write_stream on " << stream->url << "\n"; nout << "Unexpected write_stream on " << stream->url << "\n";

View File

@ -14,7 +14,7 @@
express:c downloader:c pandaexpress:m \ express:c downloader:c pandaexpress:m \
pystub pystub
#define OSX_SYS_FRAMEWORKS Foundation AppKit #define OSX_SYS_FRAMEWORKS Foundation AppKit Carbon
#define SOURCES \ #define SOURCES \
panda3d.cxx panda3d.h panda3d.I panda3d.cxx panda3d.h panda3d.I

View File

@ -265,7 +265,6 @@ run(int argc, char *argv[]) {
retval = GetMessage(&msg, NULL, 0, 0); retval = GetMessage(&msg, NULL, 0, 0);
} }
cerr << "WM_QUIT\n";
// WM_QUIT has been received. Terminate all instances, and fall // WM_QUIT has been received. Terminate all instances, and fall
// through. // through.
while (!_instances.empty()) { while (!_instances.empty()) {
@ -288,7 +287,26 @@ run(int argc, char *argv[]) {
} }
} }
#endif #elif defined(__APPLE__)
// OSX really prefers to own the main loop, so we install a timer to
// call out to our instances and getters, rather than polling within
// the event loop as we do in the Windows case, above.
EventLoopRef main_loop = GetMainEventLoop();
EventLoopTimerUPP timer_upp = NewEventLoopTimerUPP(st_timer_callback);
EventLoopTimerRef timer;
EventTimerInterval interval = 200 * kEventDurationMillisecond;
InstallEventLoopTimer(main_loop, interval, interval,
timer_upp, this, &timer);
RunApplicationEventLoop();
RemoveEventLoopTimer(timer);
// Terminate all instances, and fall through.
while (!_instances.empty()) {
P3D_instance *inst = *(_instances.begin());
delete_instance(inst);
}
#else // _WIN32, __APPLE__
// Now wait while we process pending requests. // Now wait while we process pending requests.
while (!_instances.empty()) { while (!_instances.empty()) {
@ -302,6 +320,8 @@ run(int argc, char *argv[]) {
run_getters(); run_getters();
} }
#endif // _WIN32, __APPLE__
// All instances have finished; we can exit. // All instances have finished; we can exit.
unload_plugin(); unload_plugin();
return 0; return 0;
@ -642,17 +662,17 @@ create_instance(const string &p3d, P3D_window_type window_type,
// Build up the token list. // Build up the token list.
pvector<P3D_token> tokens; pvector<P3D_token> tokens;
P3D_token token;
string log_basename; string log_basename;
if (!_log_dirname.empty()) { if (!_log_dirname.empty()) {
// Generate output to a logfile. // Generate output to a logfile.
log_basename = p3d_filename.get_basename_wo_extension(); log_basename = p3d_filename.get_basename_wo_extension();
P3D_token token;
token._keyword = "log_basename"; token._keyword = "log_basename";
token._value = log_basename.c_str(); token._value = log_basename.c_str();
tokens.push_back(token); tokens.push_back(token);
} else { } else {
// Send output to the console. // Send output to the console.
P3D_token token;
token._keyword = "console_output"; token._keyword = "console_output";
token._value = "1"; token._value = "1";
tokens.push_back(token); tokens.push_back(token);
@ -801,7 +821,7 @@ report_downloading_package(P3D_instance *instance) {
P3D_object *display_name = P3D_object_get_property(obj, "downloadPackageDisplayName"); P3D_object *display_name = P3D_object_get_property(obj, "downloadPackageDisplayName");
if (display_name == NULL) { if (display_name == NULL) {
cerr << "no name: " << obj << "\n"; cerr << "Downloading package.\n";
return; return;
} }
@ -828,6 +848,48 @@ report_download_complete(P3D_instance *instance) {
} }
} }
#ifdef __APPLE__
////////////////////////////////////////////////////////////////////
// Function: Panda3D::st_timer_callback
// Access: Private, Static
// Description: Installed as a timer on the event loop, so we can
// process local events, in the Apple implementation.
////////////////////////////////////////////////////////////////////
pascal void Panda3D::
st_timer_callback(EventLoopTimerRef timer, void *user_data) {
((Panda3D *)user_data)->timer_callback(timer);
}
#endif // __APPLE__
#ifdef __APPLE__
////////////////////////////////////////////////////////////////////
// Function: Panda3D::timer_callback
// Access: Private
// Description: Installed as a timer on the event loop, so we can
// process local events, in the Apple implementation.
////////////////////////////////////////////////////////////////////
void Panda3D::
timer_callback(EventLoopTimerRef timer) {
// Check for new requests from the Panda3D plugin.
P3D_instance *inst = P3D_check_request(false);
while (inst != (P3D_instance *)NULL) {
P3D_request *request = P3D_instance_get_request(inst);
if (request != (P3D_request *)NULL) {
handle_request(request);
}
inst = P3D_check_request(false);
}
// Check the download tasks.
run_getters();
// If we're out of instances, exit the application.
if (_instances.empty()) {
QuitApplicationEventLoop();
}
}
#endif // __APPLE__
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: Panda3D::URLGetter::Constructor // Function: Panda3D::URLGetter::Constructor
// Access: Public // Access: Public
@ -844,6 +906,7 @@ URLGetter(P3D_instance *instance, int unique_id,
HTTPClient *http = HTTPClient::get_global_ptr(); HTTPClient *http = HTTPClient::get_global_ptr();
_channel = http->make_channel(false); _channel = http->make_channel(false);
// _channel->set_download_throttle(true);
if (_post_data.empty()) { if (_post_data.empty()) {
_channel->begin_get_document(_url); _channel->begin_get_document(_url);
} else { } else {

View File

@ -68,6 +68,11 @@ private:
void report_downloading_package(P3D_instance *instance); void report_downloading_package(P3D_instance *instance);
void report_download_complete(P3D_instance *instance); void report_download_complete(P3D_instance *instance);
#ifdef __APPLE__
static pascal void st_timer_callback(EventLoopTimerRef timer, void *user_data);
void timer_callback(EventLoopTimerRef timer);
#endif
private: private:
string _root_dir; string _root_dir;
string _log_dirname; string _log_dirname;