#include "oneshot.h" /****************** * HERE BE DRAGONS ******************/ #include "eventthread.h" #include "debugwriter.h" #include //OS-Specific code #if defined _WIN32 #define OS_W32 #define WIN32_LEAN_AND_MEAN #define SECURITY_WIN32 #include #include #include #include #elif defined __APPLE__ #define OS_OSX #include #elif defined __linux__ #define OS_LINUX #include #include #include #include class GtkWidget; typedef enum { GTK_MESSAGE_INFO, GTK_MESSAGE_WARNING, GTK_MESSAGE_QUESTION, GTK_MESSAGE_ERROR } GtkMessageType; typedef enum { GTK_BUTTONS_NONE, GTK_BUTTONS_OK, GTK_BUTTONS_CLOSE, GTK_BUTTONS_CANCEL, GTK_BUTTONS_YES_NO, GTK_BUTTONS_OK_CANCEL } GtkButtonsType; typedef enum { GTK_RESPONSE_NONE = -1, GTK_RESPONSE_REJECT = -2, GTK_RESPONSE_ACCEPT = -3, GTK_RESPONSE_DELETE_EVENT = -4, GTK_RESPONSE_OK = -5, GTK_RESPONSE_CANCEL = -6, GTK_RESPONSE_CLOSE = -7, GTK_RESPONSE_YES = -8, GTK_RESPONSE_NO = -9, GTK_RESPONSE_APPLY = -10, GTK_RESPONSE_HELP = -11 } GtkResponseType; #else #error "Operating system not detected." #endif struct OneshotPrivate { //Main SDL window SDL_Window *window; //String data std::string lang; std::string userName; std::string savePath; //Dialog text std::string txtYes; std::string txtNo; #if defined OS_LINUX //GTK+ void *libgtk; void (*gtk_init)(int *argc, char ***argv); GtkWidget *(*gtk_message_dialog_new)(void *parent, int flags, GtkMessageType type, GtkButtonsType buttons, const char *message_format, ...); void (*gtk_window_set_title)(GtkWidget *window, const char *title); GtkResponseType (*gtk_dialog_run)(GtkWidget *dialog); void (*gtk_widget_destroy)(GtkWidget *widget); void (*gtk_main_quit)(); void (*gtk_main)(); unsigned int (*gdk_threads_add_idle)(int (*function)(void *data), void *data); #endif OneshotPrivate() : window(0) #if defined OS_LINUX ,libgtk(0) #endif { } ~OneshotPrivate() { #ifdef OS_LINUX if (libgtk) dlclose(libgtk); #endif } }; //OS-SPECIFIC FUNCTIONS #if defined OS_LINUX struct linux_DialogData { //Input OneshotPrivate *p; int type; const char *body; const char *title; //Output bool result; }; static int linux_dialog(void *rawData) { linux_DialogData *data = reinterpret_cast(rawData); OneshotPrivate *p = data->p; //Determine correct flags GtkMessageType gtktype; GtkButtonsType gtkbuttons = GTK_BUTTONS_OK; switch (data->type) { case Oneshot::MSG_INFO: gtktype = GTK_MESSAGE_INFO; break; case Oneshot::MSG_YESNO: gtktype = GTK_MESSAGE_QUESTION; gtkbuttons = GTK_BUTTONS_YES_NO; break; case Oneshot::MSG_WARN: gtktype = GTK_MESSAGE_WARNING; break; case Oneshot::MSG_ERR: gtktype = GTK_MESSAGE_ERROR; break; default: p->gtk_main_quit(); return 0; } //Display dialog and get result GtkWidget *dialog = p->gtk_message_dialog_new(0, 0, gtktype, gtkbuttons, data->body); p->gtk_window_set_title(dialog, data->title); int result = p->gtk_dialog_run(dialog); p->gtk_widget_destroy(dialog); //Interpret result and return data->result = (result == GTK_RESPONSE_OK || result == GTK_RESPONSE_YES); p->gtk_main_quit(); return 0; } #elif defined OS_W32 /* Convert WCHAR pointer to std::string */ static std::string w32_fromWide(const WCHAR *ustr) { std::string result; int size = WideCharToMultiByte(CP_UTF8, 0, ustr, -1, 0, 0, 0, 0); if (size > 0) { CHAR *str = new CHAR[size]; if (WideCharToMultiByte(CP_UTF8, 0, ustr, -1, str, size, 0, 0) == size) result = str; delete [] str; } return result; } /* Convert WCHAR pointer from const char* */ static WCHAR *w32_toWide(const char *str) { if (str) { int size = MultiByteToWideChar(CP_UTF8, 0, str, -1, 0, 0); if (size > 0) { WCHAR *ustr = new WCHAR[size]; if (MultiByteToWideChar(CP_UTF8, 0, str, -1, ustr, size) == size) return ustr; delete [] ustr; } } //Return empty string WCHAR *ustr = new WCHAR[1]; *ustr = 0; return ustr; } #endif Oneshot::Oneshot(const RGSSThreadData &threadData) { p = new OneshotPrivate(); p->window = threadData.window; p->savePath = threadData.config.commonDataPath.substr(0, threadData.config.commonDataPath.size() - 1); /******************** * USERNAME/SAVE PATH ********************/ #if defined OS_W32 //Get language code WCHAR wlang[9]; GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, wlang, sizeof(wlang) / sizeof(WCHAR)); p->lang = w32_fromWide(wlang); //Get user's name ULONG size = 0; GetUserNameEx(NameDisplay, 0, &size); if (GetLastError() == ERROR_MORE_DATA) { //Get their full (display) name WCHAR *name = new WCHAR[size]; GetUserNameEx(NameDisplay, name, &size); p->userName = w32_fromWide(name); delete [] name; } else { //Get their login name DWORD size2 = 0; GetUserName(0, &size2); if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { WCHAR *name = new WCHAR[size2]; GetUserName(name, &size2); p->userName = w32_fromWide(name); delete [] name; } } #else //Get language code const char *lc_all = getenv("LC_ALL"); const char *lang = getenv("LANG"); const char *code = (lc_all ? lc_all : lang); if (code) { //find first non alphanumeric character, copy language code int end = 0; for (; code[end] && (code[end] >= 'a' && code[end] <= 'z'); ++end) {} p->lang = std::string(code, end); } else p->lang = "en"; //Get user's name struct passwd *pwd = getpwuid(getuid()); if (pwd) { if (pwd->pw_gecos && pwd->pw_gecos[0] && pwd->pw_gecos[0] != ',') { //Get the user's full name int comma = 0; for (; pwd->pw_gecos[comma] && pwd->pw_gecos[comma] != ','; ++comma) {} p->userName = std::string(pwd->pw_gecos, comma); } else p->userName = pwd->pw_name; } #endif /********** * MSGBOX **********/ #ifdef OS_LINUX #define LOAD_FUNC(name) *reinterpret_cast(&p->name) = dlsym(p->libgtk, #name) //Attempt to link to gtk (prefer gtk2 over gtk3 until I can figure that message box icon out) static const char *gtklibs[] = { "libgtk-x11-2.0.so", "libgtk-3.0.so", }; for (size_t i = 0; i < ARRAY_SIZE(gtklibs); ++i) { if (!(p->libgtk = dlopen("libgtk-x11-2.0.so", RTLD_NOW))) p->libgtk = dlopen("libgtk-3.0.so", RTLD_NOW); if (p->libgtk) { //Load functions LOAD_FUNC(gtk_init); LOAD_FUNC(gtk_message_dialog_new); LOAD_FUNC(gtk_window_set_title); LOAD_FUNC(gtk_dialog_run); LOAD_FUNC(gtk_widget_destroy); LOAD_FUNC(gtk_main_quit); LOAD_FUNC(gtk_main); LOAD_FUNC(gdk_threads_add_idle); if (p->gtk_init && p->gtk_message_dialog_new && p->gtk_window_set_title && p->gtk_dialog_run && p->gtk_widget_destroy && p->gtk_main_quit && p->gtk_main && p->gdk_threads_add_idle) { p->gtk_init(0, 0); } else { dlclose(p->libgtk); p->libgtk = 0; } } if (p->libgtk) break; } #undef LOAD_FUNC #endif /******** * MISC ********/ #if defined OS_W32 //Get windows version OSVERSIONINFOW version; ZeroMemory(&version, sizeof(version)); version.dwOSVersionInfoSize = sizeof(version); GetVersionEx(&version); #endif } Oneshot::~Oneshot() { delete p; } const std::string &Oneshot::lang() const { return p->lang; } const std::string &Oneshot::userName() const { return p->userName; } const std::string &Oneshot::savePath() const { return p->savePath; } void Oneshot::setYesNo(const char *yes, const char *no) { p->txtYes = yes; p->txtNo = no; } bool Oneshot::msgbox(int type, const char *body, const char *title) { #if defined OS_W32 //Get native window handle SDL_SysWMinfo wminfo; SDL_version version; SDL_VERSION(&version); wminfo.version = version; SDL_GetWindowWMInfo(p->window, &wminfo); HWND hwnd = wminfo.info.win.window; //Construct flags UINT flags = 0; switch (type) { case MSG_INFO: flags = MB_ICONINFORMATION; break; case MSG_YESNO: flags = MB_ICONQUESTION | MB_YESNO; break; case MSG_WARN: flags = MB_ICONWARNING; break; case MSG_ERR: flags = MB_ICONERROR; break; } //Create message box WCHAR *wbody = w32_toWide(body); WCHAR *wtitle = w32_toWide(title); int result = MessageBoxW(hwnd, wbody, wtitle, flags); delete [] title; delete [] body; //Interpret result return (result == IDOK || result == IDYES); #else #if defined OS_LINUX if (p->libgtk) { linux_DialogData data = {p, type, body, title, 0}; p->gdk_threads_add_idle(linux_dialog, &data); p->gtk_main(); return data.result; } #endif //SDL message box //Button data static const SDL_MessageBoxButtonData buttonOk = {SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, "OK"}; static const SDL_MessageBoxButtonData buttonsOk[] = {buttonOk}; SDL_MessageBoxButtonData buttonYes = {SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, p->txtYes.c_str()}; SDL_MessageBoxButtonData buttonNo = {SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 0, p->txtNo.c_str()}; SDL_MessageBoxButtonData buttonsYesNo[] = {buttonNo, buttonYes}; //Messagebox data SDL_MessageBoxData data; data.window = p->window; data.colorScheme = 0; data.title = title; data.message = body; //Set type switch (type) { case MSG_INFO: case MSG_YESNO: data.flags = SDL_MESSAGEBOX_INFORMATION; break; case MSG_WARN: data.flags = SDL_MESSAGEBOX_WARNING; break; case MSG_ERR: data.flags = SDL_MESSAGEBOX_WARNING; break; } //Set buttons switch (type) { case MSG_INFO: case MSG_WARN: case MSG_ERR: data.numbuttons = 1; data.buttons = buttonsOk; break; case MSG_YESNO: data.numbuttons = 2; data.buttons = buttonsYesNo; break; } //Show messagebox int button; SDL_ShowMessageBox(&data, &button); return button ? true : false; #endif }