#include "window.h" #include "../file.h" #include "../os.h" #ifdef ARGUI_WINDOWS #undef bind #include #include #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