/* vim:expandtab:ts=2 sw=2: */ /* Grafx2 - The Ultimate 256-color bitmap paint program Copyright 2020 Thomas Bernard Copyright 2011 Pawel Góralski Copyright 2008 Yves Rizoud Copyright 2008 Franck Charlet Copyright 2007 Adrien Destugues Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud) Grafx2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. Grafx2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Grafx2; if not, see */ ////////////////////////////////////////////////////////////////////////////// ///@file osdep.c /// OS Dependend code ////////////////////////////////////////////////////////////////////////////// #if defined(USE_SDL) || defined(USE_SDL2) #include #elif !defined(WIN32) #include #endif #if defined(WIN32) #include #endif #if defined(__macosx__) || defined(__FreeBSD__) || defined(__OpenBSD__) #include #include #elif defined(__NetBSD__) #include #elif defined (__linux__) || defined(__SYLLABLE__) #include #elif defined (__HAIKU__) #include "haiku.h" #elif defined (__MINT__) #include #include #include #elif defined(__AROS__) #include #include #include #endif #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) #include #include #endif #if defined(__macosx__) #import #import #endif #if defined(USE_X11) #include extern Display * X11_display; extern Window X11_window; #elif defined(__macosx__) const char * get_paste_board(void); #endif #if defined(USE_X11) || defined(USE_SDL) || defined(USE_SDL2) #include #endif #ifdef _MSC_VER #define strdup _strdup #endif #include "struct.h" #include "input.h" #include "screen.h" #include "unicode.h" #include "gfx2log.h" #include "gfx2mem.h" dword GFX2_GetTicks(void) { #if defined(USE_SDL) || defined(USE_SDL2) return SDL_GetTicks(); #elif defined(WIN32) return GetTickCount(); #else struct timeval tv; if (gettimeofday(&tv, NULL) < 0) return 0; return tv.tv_sec * 1000 + tv.tv_usec / 1000; #endif } void GFX2_OpenURL(const char * buffer, unsigned int len) { #if defined(WIN32) (void)len; /*HINSTANCE hInst = */ShellExecuteA(NULL, "open", buffer, NULL, NULL, SW_SHOWNORMAL); #elif defined(__macosx__) OSStatus ret; CFURLRef url = CFURLCreateWithBytes ( NULL, // allocator (UInt8*)buffer, // URLBytes len, // length kCFStringEncodingASCII, // encoding NULL // baseURL ); ret = LSOpenCFURLRef(url,0); if (ret != noErr) GFX2_Log(GFX2_ERROR, "LSOpenCFURLRef() returned %d\n", ret); CFRelease(url); #elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) int ret; char command[256]; // use the xdg-open command to open the url in the default browser (void)len; snprintf(command, sizeof(command), "xdg-open \"%s\"", buffer); ret = system(command); if (ret < 0) GFX2_Log(GFX2_ERROR, "system('%s') FAILED\n", command); else if (ret == 127) GFX2_Log(GFX2_ERROR, "system() FAILED to execute shell\n"); #else // TODO : HAIKU, MINT, etc. (void)len; (void)buffer; GFX2_Log(GFX2_WARNING, "URL open not supported yet on this system.\n"); #endif } qword GFX2_DiskFreeSpace(const char * path) { #if defined(__WIN32__) || defined(WIN32) ULARGE_INTEGER tailleU, totalbytes, totalfreebytes; if (GetDiskFreeSpaceExA(path, &tailleU, &totalbytes, &totalfreebytes)) { GFX2_Log(GFX2_DEBUG, "%s: %luMB free for GrafX2 (total %luMB, %luMB free)\n", path, (unsigned long)(tailleU.QuadPart >> 20), (unsigned long)(totalbytes.QuadPart >> 20), (unsigned long)(totalfreebytes.QuadPart >> 20)); return tailleU.QuadPart; } else { GFX2_Log(GFX2_ERROR, "GetDiskFreeSpaceExA() failed\n"); return 0; } #elif defined(__linux__) || defined(__macosx__) || defined(__FreeBSD__) || defined(__SYLLABLE__) || defined(__AROS__) || defined(__OpenBSD__) struct statfs disk_info; statfs(path ,&disk_info); return (qword) disk_info.f_bfree * (qword) disk_info.f_bsize; #elif defined(__NetBSD__) struct statvfs disk_info; statvfs(path, &disk_info); return (qword) disk_info.f_bfree * (qword) disk_info.f_bsize; #elif defined(__HAIKU__) return haiku_get_free_space(path); #elif defined (__MINT__) _DISKINFO drvInfo; Dfree(&drvInfo, 0); //number of free clusters*sectors per cluster*bytes per sector; // reports current drive return drvInfo.b_free*drvInfo.b_clsiz*drvInfo.b_secsiz; #else #define NODISKSPACESUPPORT // Free disk space is only for shows. Other platforms can display 0. #if !defined(__SWITCH__) #warning "Missing code for your platform !!! Check and correct please :)" #endif return 0; #endif } #if defined(__macosx__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__SWITCH__) #if defined(__OpenBSD__) #include #endif #include #include #elif defined(__BEOS__) || defined(__HAIKU__) #include #elif defined(__AROS__) || defined(__amigaos4__) || defined(__MORPHOS__) || defined(__amigaos__) #include #elif defined(__MINT__) #include #include #elif defined(__SKYOS__) #include #elif !defined(WIN32) #include // sysinfo() for free RAM #endif #if defined (__MINT__) // atari have two kinds of memory // standard and fast ram void Atari_Memory_free(unsigned long *stRam, unsigned long *ttRam){ *stRam = Mxalloc(-1L, 0); *ttRam = Mxalloc(-1L, 1); } #else // Indique quelle est la mémoire disponible unsigned long Memory_free(void) { // Memory is no longer relevant. If there is ANY problem or doubt here, // you can simply return 10*1024*1024 (10Mb), to make the "Pages"something // memory allocation functions happy. // However, it is still a good idea to make a proper function if you can... // If Grafx2 thinks the memory is full, weird things may happen. And if memory // ever becomes full and you're still saying there are 10MB free here, the // program will crash without saving any picture backup ! You've been warned... #if defined(WIN32) #if _WIN32_WINNT >= _WIN32_WINNT_WIN2K // GlobalMemoryStatusEx() is supported since Windows 2000 MEMORYSTATUSEX mstt; mstt.dwLength = sizeof(mstt); if (GlobalMemoryStatusEx(&mstt)) { GFX2_Log(GFX2_DEBUG, "Phys %lu / %luMB, Page %lu / %luMB, Virtual %lu / %luMB\n", (unsigned long)(mstt.ullAvailPhys >> 20), (unsigned long)(mstt.ullTotalPhys >> 20), (unsigned long)(mstt.ullAvailPageFile >> 20), (unsigned long)(mstt.ullTotalPageFile >> 20), (unsigned long)(mstt.ullAvailVirtual >> 20), (unsigned long)(mstt.ullTotalVirtual >> 20)); return mstt.ullAvailPhys; } else { GFX2_Log(GFX2_ERROR, "GlobalMemoryStatusEx() failed\n"); return 10*1024*1024; } #else MEMORYSTATUS mstt; mstt.dwLength = sizeof(MEMORYSTATUS); GlobalMemoryStatus(&mstt); return mstt.dwAvailPhys; #endif #elif defined(__macosx__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) int mib[2]; int maxmem; size_t len; mib[0] = CTL_HW; mib[1] = HW_USERMEM; len = sizeof(maxmem); sysctl(mib,2,&maxmem,&len,NULL,0); return maxmem; #elif defined(__HAIKU__) || defined(__BEOS__) int pages; system_info systemInfo; get_system_info(&systemInfo); pages = systemInfo.max_pages - systemInfo.used_pages; return pages * B_PAGE_SIZE; #elif defined(__AROS__) || defined(__MORPHOS__) || defined(__amigaos__) return AvailMem(MEMF_ANY); #elif defined(__linux__) struct sysinfo info; sysinfo(&info); return info.freeram*info.mem_unit; #else // AvailMem is misleading on os4 (os4 caches stuff in memory that you can still allocate) #if defined(__SWITCH__) // There is some way to get memory information on switch (see include switch/kernel/svc.h svcGetInfo svcGetSystemInfo) // but the usage is a bit confusing for the first and the later need privilege to be used. // If you come here with a solution, you'r welcome. For now we just return the default value. #elif #warning "There is missing code there for your platform ! please check and correct :)" #endif return 10*1024*1024; #endif } #endif #if defined(WIN32) /** * Converts an unicode string to UTF8 * @param str the unicode string * @param utf8len pointer to receive the utf8 string length (excluding null terminator) * @return malloc'ed UTF8 string */ char * Unicode_to_utf8(const word * str, size_t * utf8len) { size_t unicode_len = Unicode_strlen(str); int len = WideCharToMultiByte(CP_UTF8, 0, str, unicode_len, NULL, 0, NULL, NULL); if (len <= 0) { GFX2_Log(GFX2_WARNING, "WideCharToMultiByte() failed\n"); return NULL; } else { char * utf8 = (char *)GFX2_malloc(len+1); if (utf8 == NULL) return NULL; if (WideCharToMultiByte(CP_UTF8, 0, str, unicode_len, utf8, len, NULL, NULL) <= 0) { DWORD error = GetLastError(); GFX2_Log(GFX2_WARNING, "WideCharToMultiByte() failed error=%u\n", error); free(utf8); return NULL; } if (utf8len != NULL) *utf8len = (size_t)len; utf8[len] = '\0'; return utf8; } } /** * UTF8 to Unicode (16bits per character). * use MultiByteToWideChar(CP_UTF8, ...) under WIN32. * Note : For UTF-8, dwFlags must be set to either 0 or MB_ERR_INVALID_CHARS. */ void Unicode_from_utf8(const char * utf8, word * unicode, size_t unicodelen) { int r = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, (LPWSTR)unicode, unicodelen); if (r == 0) { GFX2_Log(GFX2_ERROR, "MultiByteToWideChar(CP_UTF8, \"%s\", ...) failed with error #%u\n", utf8, GetLastError()); unicode[0] = 0; } } #endif /** * Get clipboard text content. * Inspired from http://www.libsdl.org/projects/scrap/ * @param unicode NULL for pure ANSI working, or a pointer to get Unicode text data * that should be free()'d by the caller * @return a ANSI C string that should be free()'d by the caller */ char * GFX2_GetTextClipboard(word * * unicode) { #ifdef WIN32 char* dst = NULL; if (OpenClipboard(GFX2_Get_Window_Handle())) { HANDLE hMem; if ( IsClipboardFormatAvailable(CF_TEXT) ) { char *src; hMem = GetClipboardData(CF_TEXT); if ( hMem != NULL ) { src = (char *)GlobalLock(hMem); dst = strdup(src); GlobalUnlock(hMem); } } if (unicode != NULL && IsClipboardFormatAvailable(CF_UNICODETEXT) ) { word * src; hMem = GetClipboardData(CF_UNICODETEXT); if ( hMem != NULL ) { src = (word *)GlobalLock(hMem); *unicode = Unicode_strdup(src); GlobalUnlock(hMem); } } CloseClipboard(); } return dst; #elif defined(__AROS__) struct IFFHandle *iff = NULL; struct ContextNode *cn; long error=0, unitnumber=0; char *dst = NULL; if (!(iff = AllocIFF ())) { goto bye; } if (!(iff->iff_Stream = (IPTR) OpenClipboard (unitnumber))) { goto bye; } InitIFFasClip (iff); if ((error = OpenIFF (iff, IFFF_READ)) != 0) { goto bye; } if ((error = StopChunk(iff, ID_FTXT, ID_CHRS)) != 0) { goto bye; } while(1) { error = ParseIFF(iff,IFFPARSE_SCAN); if (error) break; // we're reading only the 1st chunk cn = CurrentChunk(iff); if (cn && (cn->cn_Type == ID_FTXT) && (cn->cn_ID == ID_CHRS)) { if ((dst = malloc(cn->cn_Size + 1)) != NULL) { dst[0] = '\0'; if ((ReadChunkBytes(iff,dst,cn->cn_Size)) > 0) { dst[cn->cn_Size] = '\0'; } } } } bye: if (iff) { CloseIFF (iff); if (iff->iff_Stream) CloseClipboard ((struct ClipboardHandle *)iff->iff_Stream); FreeIFF (iff); } return dst; #elif defined __HAIKU__ if (unicode) *unicode = NULL; return haiku_get_clipboard(); #elif defined(USE_X11) || defined(__macosx__) || defined(USE_SDL2) || (defined(USE_SDL) && defined(SDL_VIDEO_DRIVER_X11) && !defined(NO_X11)) if (unicode) *unicode = NULL; #if defined(USE_SDL2) if (!SDL_HasClipboardText()) { return NULL; } else { char * utf8_str = SDL_GetClipboardText(); if (utf8_str != NULL) { #elif defined(USE_X11) || (defined(USE_SDL) && defined(SDL_VIDEO_DRIVER_X11) && !defined(NO_X11)) { int i; Atom selection; Window selection_owner; #if defined(SDL_VIDEO_DRIVER_X11) Display * X11_display; Window X11_window; int old_wmevent_state; if (!GFX2_Get_X11_Display_Window(&X11_display, &X11_window)) { GFX2_Log(GFX2_ERROR, "Failed to get X11 display and window\n"); return NULL; } if (X11_display == NULL) { char video_driver_name[32]; GFX2_Log(GFX2_WARNING, "X11 display is NULL. X11 is needed for Copy/Paste. SDL video driver is currently %s\n", SDL_VideoDriverName(video_driver_name, sizeof(video_driver_name))); return NULL; } #endif selection = XInternAtom(X11_display, "CLIPBOARD", False); selection_owner = XGetSelectionOwner(X11_display, selection); if (selection_owner == None) { selection = XInternAtom(X11_display, "PRIMARY", False); selection_owner = XGetSelectionOwner(X11_display, selection); } if (selection_owner == None) return NULL; #if defined(USE_SDL) old_wmevent_state = SDL_EventState(SDL_SYSWMEVENT, SDL_QUERY); SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); #endif XConvertSelection(X11_display, selection, XInternAtom(X11_display, "UTF8_STRING", False), XInternAtom(X11_display, "GFX2_CLIP", False), /* Property */ X11_window, CurrentTime); // wait for the event to be received for(i = 0; X11_clipboard == NULL && i < 10; i++) { Get_input(20); } #if defined(USE_SDL) SDL_EventState(SDL_SYSWMEVENT, old_wmevent_state); #endif if (X11_clipboard != NULL) { char * utf8_str = X11_clipboard; X11_clipboard = NULL; X11_clipboard_size = 0; X11_clipboard_type = X11_CLIPBOARD_NONE; #else { // mac OS without X11 const char * utf8_str = get_paste_board(); if (utf8_str != NULL) { #endif // UTF8 -> UTF16 and UTF8 -> ANSI conversions #if defined(ENABLE_FILENAMES_ICONV) if (unicode != NULL) { char * input = (char *)utf8_str; size_t inbytesleft = strlen(utf8_str); char * output; size_t outbytesleft; size_t r; *unicode = (word *)malloc(2 * inbytesleft + 2); if (*unicode != NULL) { output = (char *)*unicode; outbytesleft = 2 * inbytesleft; r = iconv(cd_utf16, &input, &inbytesleft, &output, &outbytesleft); if (r != (size_t)-1) { output[1] = output[0] = '\0'; } else { GFX2_Log(GFX2_WARNING, "Unicode conversion of clipboard text failed\n"); free(*unicode); *unicode = NULL; } } } { char * ansi_str; char * input = (char *)utf8_str; size_t inbytesleft = strlen(utf8_str); char * output; size_t outbytesleft; size_t r; ansi_str = (char *)malloc(inbytesleft + 1); if (ansi_str != NULL) { output = ansi_str; outbytesleft = inbytesleft; r = iconv(cd, &input, &inbytesleft, &output, &outbytesleft); if (r != (size_t)-1) { *output = '\0'; #if defined(USE_X11) free(utf8_str); #elif defined(USE_SDL2) SDL_free(utf8_str); #endif return ansi_str; } else { GFX2_Log(GFX2_WARNING, "ANSI conversion of clipboard text failed\n"); free(ansi_str); } } } #endif // we can get there if the charset conversions failed // return the uf8_string, that's better than nothing #if defined(USE_X11) return utf8_str; #elif defined(USE_SDL2) { char * return_str = strdup(utf8_str); SDL_free(utf8_str); return return_str; } #else // mac OS without X11 return strdup(utf8_str); #endif } } return NULL; #else // Not implemented (no standard) on Linux systems. Maybe someday... if (unicode) *unicode = NULL; return NULL; #endif } #if defined(WIN32) void GFX2_GetShortPathName(char * shortname, size_t shortname_len, const word * longname) { DWORD short_len = GetShortPathNameW((WCHAR *)longname, NULL, 0); if (short_len > 0) { WCHAR * temp_str = (WCHAR *)GFX2_malloc(short_len * sizeof(WCHAR)); short_len = GetShortPathNameW((WCHAR *)longname, temp_str, short_len); if (short_len > 0) { DWORD i; for (i = 0; i < short_len && temp_str[i] != 0; i++) shortname[i] = temp_str[i]; shortname[i] = '\0'; } free(temp_str); } if (short_len == 0) { // generate a temporary ansi name int i; for (i = 0; (i < (int)shortname_len - 1) && (longname[i] != 0); i++) { shortname[i] = (longname[i] < 256) ? (byte)longname[i] : '_'; } shortname[i] = '\0'; } } #endif