CPlusPlus for Gideros Studio Help
Here you will find various resources to help learn C++ for people who wish to help with Gideros Studio development.
WIN32
KNOWNFOLDERID
platform-win32.cpp
refs:
- KNOWNFOLDERID: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
- shlobj_core.h header: https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/
- SHGetKnownFolderPath function (shlobj_core.h): https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath
- SO (cc-by): https://stackoverflow.com/a/50660989/870125
C:\dev\gideros_hgy29\libgid\src\win32\platform-win32.cpp
Convert wstring <-> string
platform-win32.cpp
refs:
- SO (cc-by): https://stackoverflow.com/a/18374698/870125
C:\dev\gideros_hgy29\libgid\src\win32\platform-win32.cpp
#include <locale> // new 20221014 XXX
#include <codecvt> // new 20221014 XXX
...
std::wstring s2ws(const std::string& str)
{
using convert_typeX = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_typeX, wchar_t> converterX;
return converterX.from_bytes(str);
}
std::string ws2s(const std::wstring& wstr)
{
using convert_typeX = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_typeX, wchar_t> converterX;
return converterX.to_bytes(wstr);
}
win32 minimum, maximum screen size
platform-win32.cpp
refs:
- https://www.youtube.com/watch?v=-kg4TG7GoYI
- https://gamedev.net/forums/topic/569148-c-windows-api-minimum-resize-dimensions/4638297/
- SO (cc-by): https://stackoverflow.com/q/19035481/870125
- SO (cc-by): https://stackoverflow.com/a/22261818/870125
- https://learn.microsoft.com/en-gb/windows/win32/winmsg/wm-getminmaxinfo?redirectedfrom=MSDN
- https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-wndproc
C:\dev\gideros_hgy29\libgid\src\win32\platform-win32.cpp
win32 LFS problem with separator
Recent changes to path handling (most likely [0]) caused AssetCatalogTest.create_catalog_after_loading_file to fail on WIN32.
The test relied on the resulting path to be joined with "/" as a path separator. The resulting path used both forward and back-slashes. While these do work for some API's on WIN32, mixing both in a file path isn't expected behavior in most cases, so update the tests to use native slash direction for file-paths.
* \note If you want a trailing slash, add `SEP_STR` as the last path argument,
* duplicate slashes will be cleaned up.
*/
size_t BLI_path_join(char *__restrict dst, size_t dst_len, const char *path, ...)
# define SEP '\\'
# define ALTSEP '/'
# define SEP_STR "\\"
# define ALTSEP_STR "/"
#else
# define SEP '/'
#define SEP_CHR '#'
#define SEP_STR "#"
#define EPS 0.001
#define UN_SC_KM 1000.0f
#define UN_SC_HM 100.0f
str_tmp, TEMP_STR_SIZE, "*%.9g" SEP_STR, unit->scalar / scale_pref);
if (len_num > len_max) {
len_num = len_max;
}
if (found_ofs + len_num + len_move > len_max) {
openFileDialog
platform-win32.cpp
refs:
- https://learn.microsoft.com/en-us/windows/win32/learnwin32/learn-to-program-for-windows
- https://weblogs.asp.net/kennykerr/Windows-Vista-for-Developers-_1320_-Part-6-_1320_-The-New-File-Dialogs
- https://www.codeproject.com/Articles/16678/Vista-Goodies-in-C-Using-the-New-Vista-File-Dialog
- https://learn.microsoft.com/en-us/windows/win32/shell/common-file-dialog
#include <wchar.h> // new 20221026 XXX
...
}else if ((strcmp(what, "openDirectoryDialog") == 0)
|| (strcmp(what, "openFileDialog") == 0)
|| (strcmp(what, "saveFileDialog") == 0))
{
/* TODO parse extension :-( */
// for win32 args ("title|path|extensions//help") is only 1 arg
// unlike Qt which seems to cut the | into different args
if(args.size() == 1){ // do we need to deal with "help" as arg or leave it to Qt?
// we cut the args
std::wstring s=ws(args[0].s.c_str()); // "Title|c:\\tmp|Text (*.txt);; Image (*.png)"
size_t index = s.find(L"|");
size_t index2 = s.find(L"|", index+1);
std::wstring title = s.substr(0, index);
std::wstring place = s.substr(index+1, index2-index-1);
std::wstring extension = s.substr(index2+1);
// printf("t p e:\n %ls\n %ls\n %ls\n", title.c_str(),place.c_str(),extension.c_str()); // TEST OK
DWORD dwFlags; // new 20221028 XXX
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
if (strcmp(what, "openDirectoryDialog") == 0){
/* TO DO */
/*--------------------------------------------------*/
}else if (strcmp(what, "openFileDialog") == 0){
IFileOpenDialog *pFile;
COMDLG_FILTERSPEC fileTypes[] = /* TODO parse from wstring extension :-( */
{
{ L"Text Documents", L"*.txt" },
{ L"All Files", L"*.*" },
};
// Create the FileOpenDialog object.
hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
IID_IFileOpenDialog, reinterpret_cast<void**>(&pFile));
if (SUCCEEDED(hr))
{
// get/set options
pFile->GetOptions(&dwFlags);
pFile->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
pFile->SetFileTypes(ARRAYSIZE(fileTypes), fileTypes); // SEE ABOVE fileTypes
pFile->SetFileTypeIndex(1); // index starts at 1
// pFile->SetDefaultExtension(L"obj;fbx"); // XXX
hr = pFile->SetTitle(title.c_str()); // need more check?
// set starting folder
IShellItem *pItem = NULL;
hr = SHCreateItemFromParsingName(place.c_str(), NULL, IID_IShellItem, (LPVOID *)&pItem);
if (SUCCEEDED(hr))
{
pFile->SetFolder(pItem);
pItem->Release();
pItem = NULL;
}
// Show the Open dialog box.
hr = pFile->Show(NULL);
// Get the file name from the dialog box.
if (SUCCEEDED(hr))
{
IShellItem *pItem;
hr = pFile->GetResult(&pItem);
if (SUCCEEDED(hr))
{
PWSTR pszFilePath;
hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
if (SUCCEEDED(hr))
{
r.type=gapplication_Variant::STRING;
r.s=us(pszFilePath);
rets.push_back(r);
CoTaskMemFree(pszFilePath);
}
pItem->Release();
}
}
pFile->Release();
}
CoUninitialize();
/*--------------------------------------------------*/
}else if (strcmp(what, "saveFileDialog") == 0){
/* TO DO */
/*--------------------------------------------------*/
}
}
}
/*------------------------------------------------------------------*/
}else if (strcmp(what, "temporaryDirectory") == 0)
openFileDialog Latest
platform-win32.cpp (updated and working piece of code)
Added:
- append file extension to saved files (1st extension from filters)
}else if ((strcmp(what, "openDirectoryDialog") == 0)
|| (strcmp(what, "openFileDialog") == 0)
|| (strcmp(what, "saveFileDialog") == 0))
{
if(args.size() <= 2){
/* INFO SHOWN IN GIDEROS STUDIO DEBUGGER, IMPLEMENTED IN QT, NOT NEEDED HERE? */
}
else
{
std::wstring title = ws(args[0].s.c_str());
std::wstring place = ws(args[1].s.c_str());
std::vector<std::pair<std::wstring,std::wstring>> filters;
if (args.size()>=3) {
std::wstring ext = ws(args[2].s.c_str());
while (!ext.empty()) {
std::wstring next;
size_t semicolon=ext.find(L";;");
if (semicolon!=std::wstring::npos) {
next=ext.substr(semicolon+2);
ext=ext.substr(0,semicolon);
}
size_t p1=ext.find_first_of(L'(');
size_t p2=ext.find_last_of(L')');
if ((p1!=std::wstring::npos)&&(p2!=std::wstring::npos)&&(p2>p1))
{
//Valid filter, extract label and extensions
std::wstring label=ext.substr(0,p1);
std::wstring exts=ext.substr(p1+1,p2-p1-1);
//QT uses space for extensions separator, while windows expects semicolon. Convert them.
std::replace(exts.begin(),exts.end(),L' ',L';');
filters.push_back(std::pair<std::wstring,std::wstring>(label,exts));
}
ext=next;
}
}
DWORD dwFlags;
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
COMDLG_FILTERSPEC *fileTypes=new COMDLG_FILTERSPEC[filters.size()];
for (size_t i=0;i<filters.size();i++) {
fileTypes[i].pszName=filters[i].first.c_str();
fileTypes[i].pszSpec=filters[i].second.c_str();
}
IFileDialog *pFile;
if (strcmp(what, "saveFileDialog") == 0)
hr = CoCreateInstance(CLSID_FileSaveDialog, NULL, CLSCTX_ALL,
IID_IFileSaveDialog, reinterpret_cast<void**>(&pFile));
else
hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
IID_IFileOpenDialog, reinterpret_cast<void**>(&pFile));
if (SUCCEEDED(hr))
{
bool isFolder=(strcmp(what, "openDirectoryDialog") == 0);
// get/set options
pFile->GetOptions(&dwFlags);
pFile->SetOptions(dwFlags | FOS_FORCEFILESYSTEM | (isFolder?FOS_PICKFOLDERS:0));
if (!isFolder) {
pFile->SetFileTypes(filters.size(), fileTypes);
pFile->SetFileTypeIndex(1); // index starts at 1
//pFile->SetDefaultExtension(L"obj;fbx"); // append default extension
//printf("* fileTypes *, set default extension to %ls\n", fileTypes[0].pszSpec); OK
pFile->SetDefaultExtension(fileTypes[0].pszSpec); // append default 1st extension
}
hr = pFile->SetTitle(title.c_str()); // need more check?
// set starting folder
IShellItem *pItem = NULL;
hr = SHCreateItemFromParsingName(place.c_str(), NULL, IID_IShellItem, (LPVOID *)&pItem);
if (SUCCEEDED(hr))
{
pFile->SetFolder(pItem);
pItem->Release();
pItem = NULL;
}
// Show the Open dialog box.
hr = pFile->Show(NULL);
// Get the file name from the dialog box.
if (SUCCEEDED(hr))
{
IShellItem *pItem;
hr = pFile->GetResult(&pItem);
if (SUCCEEDED(hr))
{
PWSTR pszFilePath;
hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
if (SUCCEEDED(hr))
{
r.type=gapplication_Variant::STRING;
r.s=us(pszFilePath);
rets.push_back(r);
CoTaskMemFree(pszFilePath);
}
pItem->Release();
}
}
pFile->Release();
}
CoUninitialize();
delete[] fileTypes;
}
}
/*------------------------------------------------------------------*/
}else if (strcmp(what, "temporaryDirectory") == 0)
Locate first occurrence of character in wide string
platform-win32.cpp
refs:
std::wstring gs=ws(args[0].s.c_str()); // "Title|C:/tmp/|Text (*.txt);; Image (*.png)"
size_t index = gs.find(L"|");
size_t index2 = gs.find(L"|", index+1);
printf("args0: %ls\n", gs.c_str()); // output: Title|C:/tmp/|Text (*.txt);; Image (*.png)
printf("indices: %d, %d\n", index,index2); // output 5, 13
std::wstring title = gs.substr(0, index);
std::wstring path = gs.substr(index+1, index2-index-1);
std::wstring exts = gs.substr(index2+1);
printf("t p e:\n %ls\n %ls\n %ls\n", title.c_str(),path.c_str(),exts.c_str()); // Title C:/tmp/ Text (*.txt);; Image (*.png)
application:getKeyboardModifiers()
refs:
- https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input#key-status
- https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input#keystroke-message-flags
- SO (cc-by): https://stackoverflow.com/questions/1811206/on-win32-how-to-detect-whether-a-left-shift-or-right-alt-is-pressed-using-perl
if(GetAsyncKeyState(VK_LSHIFT) & 0x8000)
; // left shift is currently down
- \libgid\src\win32\platform-win32.cpp
int getKeyboardModifiers()
{
int m=0;
if (GetKeyState(VK_CONTROL) & 0x8000) m|=GINPUT_CTRL_MODIFIER;
if (GetKeyState(VK_SHIFT) & 0x8000) m|=GINPUT_SHIFT_MODIFIER;
if (GetKeyState(VK_MENU) & 0x8000) m|=GINPUT_ALT_MODIFIER;
return m;
}
- \win32_example\win32.cpp
else if ((iMsg==WM_KEYDOWN)||(iMsg==WM_SYSKEYDOWN)) {
int m=0;
if (GetKeyState(VK_CONTROL) & 0x8000) m|=GINPUT_CTRL_MODIFIER;
if (GetKeyState(VK_SHIFT) & 0x8000) m|=GINPUT_SHIFT_MODIFIER;
if (GetKeyState(VK_MENU) & 0x8000) m|=GINPUT_ALT_MODIFIER;
if (!(lParam&0x40000000)) // don't send repeated keys XXX
ginputp_keyDown(wParam,m);
if ((iMsg==WM_KEYDOWN)||(wParam==VK_F10))
return 0;
}
else if ((iMsg==WM_KEYUP)||(iMsg==WM_SYSKEYUP)) {
int m=0;
if (GetKeyState(VK_CONTROL) & 0x8000) m|=GINPUT_CTRL_MODIFIER;
if (GetKeyState(VK_SHIFT) & 0x8000) m|=GINPUT_SHIFT_MODIFIER;
if (GetKeyState(VK_MENU) & 0x8000) m|=GINPUT_ALT_MODIFIER;
ginputp_keyUp(wParam,m);
return 0;
}
GetKeyState is for messages when you need the state at the time the message was sent.
GetAsyncKeyState is for the state right now.
The high bit will be set if it's down, and it returns a short, hence the 1000000000000000, or 0x8000 mask
- https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeystate
- https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getasynckeystate
Notes:
wparam contains the virtual key code, lparam contains the the key state vector.
mkDir
platform-win32.cpp
Tested and seems ok :-)
refs:
}else if (strcmp(what, "mkDir") == 0)
{
// new 20221114 XXX
std::wstring dir = ws(args[0].s.c_str());
if (_wmkdir(dir.c_str()) == 0)
printf("mkDir OK: %S\n", dir.c_str()); // can be useful for the w32 console
else
printf("mkDir failed or Dir may already exist: %S\n", dir.c_str()); // can be useful for the w32 console
/*------------------------------------------------------------------*/
}else if (strcmp(what, "documentDirectory") == 0)
pathfileexists
platform-win32.cpp
Tested and seems ok but not sure about what the return value should be :-/ and we can also check for access mode!
refs:
- SO (cc-by): https://stackoverflow.com/a/28011583/870125
- https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/access-waccess?view=msvc-170
}else if (strcmp(what, "pathfileexists") == 0) // new 20221116 XXX
{
if (args.size() > 0) {
std::wstring path = ws(args[0].s.c_str());
int retValue;
if (_waccess(path.c_str(), 0) == 0) { // modes: 0=Existence only, 2=Write-only, 4=Read-only, 6=Read and write
// path exists
retValue = 1;
} else {
retValue = 0;
}
r.type=gapplication_Variant::DOUBLE;
r.d=retValue;
rets.push_back(r);
}
/*------------------------------------------------------------------*/
}else if ((strcmp(what, "openDirectoryDialog") == 0)
pathfileexists Qt
platform-qt.cpp
QFile Class seems to be "kind of" the equivalent?
refs:
bool QFile::exists(const QString &fileName) Returns true if the file specified by fileName exists; otherwise returns false. Note: If fileName is a symlink that points to a non-existing file, false is returned.
I will need to mix this and _waccess.
WM_GETMINMAXINFO
win32.cpp
WIP
refs:
- SO (cc-by): https://stackoverflow.com/questions/22261804/how-to-set-minimum-window-size-using-winapi
- https://learn.microsoft.com/en-gb/windows/win32/winmsg/wm-getminmaxinfo?redirectedfrom=MSDN
LoadCursorA
platform-win32.cpp
WIP
refs:
- SO (cc-by): https://stackoverflow.com/questions/19257237/reset-cursor-in-wm-setcursor-handler-properly
- https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadcursora
else { // SET
if (strcmp(what, "cursor") == 0)
{
/* TODO */
std::string &shape = args[0].s;
// QStringList acceptedValue;
// acceptedValue << "arrow" << "upArrow" << "cross" << "wait" << "IBeam";
// acceptedValue << "sizeVer" << "sizeHor" << "sizeBDiag" << "sizeFDiag" << "sizeAll";
// acceptedValue << "blank" << "splitV" << "splitH" << "pointingHand" << "forbidden";
// acceptedValue << "whatsThis" << "busy" << "openHand" << "closedHand" << "dragCopy";
// acceptedValue << "dragMove" << "dragLink";
HCURSOR cursor;
if (shape == "arrow") {
printf("** cursor arrow\n");
cursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW);
SetCursor(cursor);
} else if (shape == "upArrow") {
printf("** cursor uparrow\n"); // LUA DEBUG OK BUT ARROW SHAPE DOESN'T CHANGE :-(
cursor = LoadCursorA(NULL, (LPCSTR)IDC_UPARROW);
SetCursor(cursor);
}
/*------------------------------------------------------------------*/
}else if (strcmp(what, "windowPosition") == 0)
modal windows dialog
Test01
refs:
- https://learn.microsoft.com/en-us/windows/win32/dlgbox/using-dialog-boxes#creating-a-modeless-dialog-box
- https://learn.microsoft.com/en-us/windows/win32/api/olectl/nf-olectl-olecreatepropertyframe#remarks
C:\dev\gideros_dev\libgid\src\win32\platform-win32.cpp
COMDLG_FILTERSPEC *fileTypes=new COMDLG_FILTERSPEC[filters.size()];
for (size_t i=0;i<filters.size();i++) {
fileTypes[i].pszName=filters[i].first.c_str();
fileTypes[i].pszSpec=filters[i].second.c_str();
}
IFileDialog *pFile;
if (strcmp(what, "saveFileDialog") == 0)
// hr = CoCreateInstance(CLSID_FileSaveDialog, NULL, CLSCTX_ALL,
// IID_IFileSaveDialog, reinterpret_cast<void**>(&pFile));
hr = CoCreateInstance(CLSID_FileSaveDialog, hwndcopy, CLSCTX_ALL,
IID_IFileSaveDialog, reinterpret_cast<void**>(&pFile));
else
// hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
// IID_IFileOpenDialog, reinterpret_cast<void**>(&pFile));
hr = CoCreateInstance(CLSID_FileOpenDialog, hwndcopy, CLSCTX_ALL,
IID_IFileOpenDialog, reinterpret_cast<void**>(&pFile));
if (SUCCEEDED(hr))
{
CBUMP
the TODOs
bump.cpp
static bool rect_getSegmentIntersectionIndices(double x, double y, double w,
double h, double x1, double y1, double x2, double y2, double &ti1,
double &ti2, double &nx1, double &ny1, double &nx2, double &ny2) {
//ti1, ti2 = ti1 or 0, ti2 or 1 TODO
double dx = x2 - x1;
double dy = y2 - y1;
...
static bool rect_detectCollision(double x1, double y1, double w1, double h1,
double x2, double y2, double w2, double h2, double goalX, double goalY,
Collision &col) {
//goalX = goalX or x1 TODO
//goalY = goalY or y1
double dx = goalX - x1, dy = goalY - y1;
...
It is time to do the TODOs ;-)