mirror of
https://github.com/Alcaro/Flips.git
synced 2026-04-07 18:06:22 -05:00
333 lines
9.7 KiB
C++
333 lines
9.7 KiB
C++
#include "window.h"
|
|
#include "../file.h"
|
|
#include "../os.h"
|
|
#ifdef ARGUI_WINDOWS
|
|
#undef bind
|
|
#include <windows.h>
|
|
#include <commdlg.h>
|
|
#define bind bind_func
|
|
#ifdef ARLIB_WUTF
|
|
#include "../wutf/wutf.h"
|
|
#endif
|
|
|
|
//Number of ugly hacks: 6
|
|
//If a status bar item is right-aligned, a space is appended.
|
|
//The status bar is created with WS_DISABLED.
|
|
//WM_SYSCOMMAND is sometimes ignored.
|
|
//I have to keep track of the mouse position so I can ignore various bogus instances of WM_MOUSEMOVE.
|
|
//I have to undefine 'bind' before including any Windows header. I suspect something is including winsock.
|
|
//Console handling under Windows is a mess. (But launching a GUI app from a Linux console isn't much better...)
|
|
|
|
//Microsoft dropped Windows XP at April 8, 2014, after an unusually long support period. That is well above two years ago.
|
|
//Vista will die on April 11, 2017. But its user count is so low I don't care about dropping that either.
|
|
//Therefore, I have no reason to continue caring about it working.
|
|
//Incompatibility levels:
|
|
//Level 0 - a feature works as intended
|
|
//Level 1 - a feature is usable, but behaves weirdly
|
|
//Level 2 - attempting to use a feature throws an error box, or reports failure in a way the program can and does handle
|
|
//Level 3 - attempting to use a feature reports success internally, but nothing happens
|
|
//Level 4 - attempting to use a feature crashes the program
|
|
//Level 5 - program won't start
|
|
//Maximum allowed incompatibility level:
|
|
//XP SP2 and older: 5
|
|
//XP SP3:
|
|
// 1 after December 8, 2013
|
|
// 2 after April 8, 2014
|
|
// 3 after August 8, 2014
|
|
// 4 after December 8, 2014
|
|
// 5 after April 8, 2015
|
|
//Vista SP0 and higher: 0
|
|
//List:
|
|
//Level 0: SetDllDirectory demands XP SP1 or higher. (But anything below SP3 is, for all intents and purposes, dead.)
|
|
//Level 1: LVCFMT_FIXED_WIDTH on the listbox is ignored before Vista
|
|
//Danger list (likely to hit):
|
|
//Level 4: printf dislikes z (size_t) size specifiers; they must be behind #ifdef DEBUG, or turned into "I" via #define
|
|
// NOTE: This is present on Vista too. z requires 7 or higher.
|
|
//Level 5: 64-bit programs dislike XP (there are 32bit Vista/7/8, but Vista is practically dead, as is 32bit 7+)
|
|
//Level 5: SRWLOCK is Vista+
|
|
|
|
//static LARGE_INTEGER timer_freq;
|
|
|
|
void window_init(int * argc, char * * argv[])
|
|
{
|
|
#ifdef ARLIB_WUTF
|
|
WuTF_enable_args(argc, argv);
|
|
#endif
|
|
|
|
for (unsigned int i=0;(*argv)[0][i];i++)
|
|
{
|
|
if ((*argv)[0][i]=='\\') (*argv)[0][i]='/';
|
|
}
|
|
|
|
_window_init_file();
|
|
_window_init_shell();
|
|
_window_init_inner();
|
|
|
|
//QueryPerformanceFrequency(&timer_freq);
|
|
}
|
|
|
|
bool window_try_init(int * argc, char * * argv[])
|
|
{
|
|
window_init(argc, argv);
|
|
return true;
|
|
}
|
|
|
|
bool window_console_avail()
|
|
{
|
|
AttachConsole(ATTACH_PARENT_PROCESS);
|
|
return GetConsoleWindow();
|
|
}
|
|
|
|
bool window_attach_console()
|
|
{
|
|
//doesn't create a new console if not launched from one, it'd go away on app exit anyways
|
|
//doesn't like being launched from cmd; cmd wants to run a new command if spawning a gui app
|
|
// I can't make it not be a gui app, that flashes a console; it acts sanely from batch files
|
|
//windows consoles are, like so much else, a massive mess
|
|
|
|
#error check whether AttachConsole attaches stdin
|
|
|
|
bool claimstdin=(GetFileType(GetStdHandle(STD_INPUT_HANDLE))==FILE_TYPE_UNKNOWN);
|
|
bool claimstdout=(GetFileType(GetStdHandle(STD_OUTPUT_HANDLE))==FILE_TYPE_UNKNOWN);
|
|
bool claimstderr=(GetFileType(GetStdHandle(STD_ERROR_HANDLE))==FILE_TYPE_UNKNOWN);
|
|
|
|
AttachConsole(ATTACH_PARENT_PROCESS);
|
|
|
|
if (claimstdin) freopen("CONIN$", "rt", stdin);
|
|
if (claimstdout) freopen("CONOUT$", "wt", stdout);
|
|
if (claimstderr) freopen("CONOUT$", "wt", stderr);
|
|
|
|
if (claimstdout) fputc('\r', stdout);
|
|
if (claimstderr) fputc('\r', stderr);
|
|
|
|
return window_console_avail();
|
|
}
|
|
|
|
#if 0
|
|
file* file::create(const char * filename)
|
|
{
|
|
//sorry Windows, no fancy features for you, you suck
|
|
return create_fs(filename);
|
|
}
|
|
|
|
bool window_message_box(const char * text, const char * title, enum mbox_sev severity, enum mbox_btns buttons)
|
|
{
|
|
UINT sev[3]={ 0, MB_ICONWARNING, MB_ICONERROR };
|
|
UINT btns[3]={ 0, MB_OKCANCEL, MB_YESNO };
|
|
int ret=MessageBox(NULL, text, title, sev[severity]|btns[buttons]|MB_TASKMODAL);
|
|
return (ret==IDOK || ret==IDYES);
|
|
}
|
|
|
|
const char * const * window_file_picker(window * parent,
|
|
const char * title,
|
|
const char * const * extensions,
|
|
const char * extdescription,
|
|
bool dylib,
|
|
bool multiple)
|
|
{
|
|
//there is no reasonable way to use the dylib flag; windows has nothing gvfs-like (okay, maybe IShellItem, but I can't get that from GetOpenFileName).
|
|
static char * * ret=NULL;
|
|
if (ret)
|
|
{
|
|
char * * del=ret;
|
|
while (*del)
|
|
{
|
|
free(*del);
|
|
del++;
|
|
}
|
|
free(ret);
|
|
ret=NULL;
|
|
}
|
|
|
|
unsigned int filterlen=strlen(extdescription)+1+0+1-1+strlen("All files")+1+strlen("*.*")+1+1;
|
|
for (unsigned int i=0;extensions[i];i++) filterlen+=2+strlen(extensions[i])+1;
|
|
char * filter=malloc(filterlen);
|
|
|
|
char * filterat=filter;
|
|
strcpy(filterat, extdescription);
|
|
filterat+=strlen(extdescription)+1;
|
|
for (unsigned int i=0;extensions[i];i++)
|
|
{
|
|
unsigned int thislen=strlen(extensions[i]);
|
|
filterat[0]='*';
|
|
filterat[1]='.';
|
|
if (*extensions[i]=='.') filterat--;
|
|
memcpy(filterat+2, extensions[i], thislen);
|
|
filterat[2+thislen]=';';
|
|
filterat+=2+thislen+1;
|
|
}
|
|
memcpy(filterat-1, "\0All files\0*.*\0", 1+strlen("All files")+1+strlen("*.*")+1+1);
|
|
|
|
OPENFILENAME ofn;
|
|
ZeroMemory(&ofn, sizeof(ofn));
|
|
ofn.lStructSize=sizeof(ofn);
|
|
ofn.hwndOwner=(parent?(HWND)parent->_get_handle():NULL);
|
|
ofn.lpstrFilter=(extensions[0] ? filter : "All files (*.*)\0*.*\0");
|
|
char * filenames=malloc(65536);
|
|
*filenames='\0';
|
|
ofn.lpstrFile=filenames;
|
|
ofn.nMaxFile=65536;
|
|
ofn.lpstrTitle=title;
|
|
ofn.Flags=OFN_HIDEREADONLY|OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_EXPLORER|(multiple?OFN_ALLOWMULTISELECT:0);
|
|
ofn.lpstrDefExt=NULL;
|
|
|
|
WCHAR cwd[MAX_PATH];
|
|
GetCurrentDirectoryW(MAX_PATH, cwd);
|
|
BOOL ok=GetOpenFileName(&ofn);
|
|
SetCurrentDirectoryW(cwd);
|
|
|
|
free(filter);
|
|
if (!ok)
|
|
{
|
|
free(filenames);
|
|
return NULL;
|
|
}
|
|
|
|
bool ismultiple=(ofn.nFileOffset && filenames[ofn.nFileOffset-1]=='\0');
|
|
if (!ismultiple)
|
|
{
|
|
ret=malloc(sizeof(char*)*2);
|
|
ret[0]=window_get_absolute_path(window_get_cwd(), filenames, true);
|
|
ret[1]=NULL;
|
|
return (const char * const *)ret;
|
|
}
|
|
filenames[ofn.nFileOffset-1]='\\';
|
|
|
|
unsigned int numfiles=0;
|
|
char * filename=filenames+ofn.nFileOffset;
|
|
while (*filename)
|
|
{
|
|
numfiles++;
|
|
filename+=strlen(filename)+1;
|
|
}
|
|
|
|
ret=malloc(sizeof(char*)*(numfiles+1));
|
|
filename=filenames+ofn.nFileOffset;
|
|
char * * retout=ret;
|
|
while (*filename)
|
|
{
|
|
unsigned int thislen=strlen(filename);
|
|
memcpy(filenames+ofn.nFileOffset, filename, thislen+1);
|
|
*retout=window_get_absolute_path(window_get_cwd(), filenames, true);
|
|
retout++;
|
|
filename+=thislen+1;
|
|
}
|
|
free(filenames);
|
|
ret[numfiles] = NULL;
|
|
|
|
return (const char * const *)ret;
|
|
}
|
|
|
|
char * window_get_absolute_path(const char * basepath, const char * path, bool allow_up)
|
|
{
|
|
return _window_native_get_absolute_path(basepath, path, allow_up);
|
|
}
|
|
|
|
|
|
|
|
bool file_read(const char * filename, void* * data, size_t * len)
|
|
{
|
|
if (!filename) return false;
|
|
HANDLE file=CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (file==INVALID_HANDLE_VALUE) return false;
|
|
DWORD readlen=GetFileSize(file, NULL);
|
|
DWORD truelen;
|
|
char* truedata=malloc(readlen+1);
|
|
ReadFile(file, truedata, readlen, &truelen, NULL);
|
|
truedata[readlen]='\0';
|
|
*data=truedata;
|
|
CloseHandle(file);
|
|
if (truelen!=readlen)
|
|
{
|
|
free(truedata);
|
|
return false;
|
|
}
|
|
if (len) *len=truelen;
|
|
return true;
|
|
}
|
|
|
|
bool file_write(const char * filename, const anyptr data, size_t len)
|
|
{
|
|
if (!filename) return false;
|
|
if (!len) return true;
|
|
HANDLE file=CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (file==INVALID_HANDLE_VALUE) return false;
|
|
DWORD truelen;
|
|
WriteFile(file, data, len, &truelen, NULL);
|
|
CloseHandle(file);
|
|
return (truelen==len);
|
|
}
|
|
|
|
bool file_read_to(const char * filename, anyptr data, size_t len)
|
|
{
|
|
if (!filename) return false;
|
|
if (!len) return true;
|
|
HANDLE file=CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (file==INVALID_HANDLE_VALUE) return false;
|
|
DWORD readlen=GetFileSize(file, NULL);
|
|
if (readlen!=len)
|
|
{
|
|
CloseHandle(file);
|
|
return false;
|
|
}
|
|
DWORD truelen;
|
|
ReadFile(file, data, len, &truelen, NULL);
|
|
CloseHandle(file);
|
|
return (len==truelen);
|
|
}
|
|
|
|
|
|
//this could be made far cleaner if . or .. was guaranteed to be first.
|
|
struct finddata {
|
|
HANDLE h;
|
|
WIN32_FIND_DATA file;
|
|
bool first;
|
|
};
|
|
|
|
void* file_find_create(const char * path)
|
|
{
|
|
if (!path) return NULL;
|
|
int pathlen=strlen(path);
|
|
char * pathcopy=malloc(pathlen+3);
|
|
memcpy(pathcopy, path, pathlen);
|
|
pathcopy[pathlen+0]='\\';
|
|
pathcopy[pathlen+1]='*';
|
|
pathcopy[pathlen+2]='\0';
|
|
struct finddata * find=malloc(sizeof(struct finddata));
|
|
find->h=FindFirstFile(pathcopy, &find->file);
|
|
free(pathcopy);
|
|
find->first=true;
|
|
if (find->h==INVALID_HANDLE_VALUE)
|
|
{
|
|
free(find);
|
|
return NULL;
|
|
}
|
|
return find;
|
|
}
|
|
|
|
bool file_find_next(void* find_, char * * path, bool * isdir)
|
|
{
|
|
if (!find_) return false;
|
|
struct finddata * find=(struct finddata*)find_;
|
|
nextfile:;
|
|
bool ok=true;
|
|
if (find->first) find->first=false;
|
|
else ok=FindNextFile(find->h, &find->file);
|
|
if (!ok) return false;
|
|
if (!strcmp(find->file.cFileName, ".")) goto nextfile;
|
|
if (!strcmp(find->file.cFileName, "..")) goto nextfile;
|
|
*path=strdup(find->file.cFileName);
|
|
*isdir=(find->file.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY);
|
|
return true;
|
|
}
|
|
|
|
void file_find_close(void* find_)
|
|
{
|
|
if (!find_) return;
|
|
struct finddata * find=(struct finddata*)find_;
|
|
FindClose(find->h);
|
|
free(find);
|
|
}
|
|
#endif
|
|
#endif
|