grafX2/src/osdep.c
2020-05-14 00:20:08 +02:00

627 lines
18 KiB
C

/* 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 <http://www.gnu.org/licenses/>
*/
//////////////////////////////////////////////////////////////////////////////
///@file osdep.c
/// OS Dependend code
//////////////////////////////////////////////////////////////////////////////
#if defined(USE_SDL) || defined(USE_SDL2)
#include <SDL.h>
#elif !defined(WIN32)
#include <sys/time.h>
#endif
#if defined(WIN32)
#include <windows.h>
#endif
#if defined(__macosx__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#include <sys/param.h>
#include <sys/mount.h>
#elif defined(__NetBSD__)
#include <sys/statvfs.h>
#elif defined (__linux__) || defined(__SYLLABLE__)
#include <sys/vfs.h>
#elif defined (__HAIKU__)
#include "haiku.h"
#elif defined (__MINT__)
#include <mint/sysbind.h>
#include <mint/osbind.h>
#include <mint/ostruct.h>
#elif defined(__AROS__)
#include <sys/mount.h>
#include <proto/iffparse.h>
#include <datatypes/textclass.h>
#endif
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
#include <stdlib.h>
#include <stdio.h>
#endif
#if defined(__macosx__)
#import <CoreFoundation/CoreFoundation.h>
#import <ApplicationServices/ApplicationServices.h>
#endif
#if defined(USE_X11)
#include <X11/Xlib.h>
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 <string.h>
#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 <sys/param.h>
#endif
#include <sys/types.h>
#include <sys/sysctl.h>
#elif defined(__BEOS__) || defined(__HAIKU__)
#include <kernel/OS.h>
#elif defined(__AROS__) || defined(__amigaos4__) || defined(__MORPHOS__) || defined(__amigaos__)
#include <proto/exec.h>
#elif defined(__MINT__)
#include <mint/osbind.h>
#include <mint/sysbind.h>
#elif defined(__SKYOS__)
#include <skyos/sysinfo.h>
#elif !defined(WIN32)
#include <sys/sysinfo.h> // 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