diff --git a/CommonCfg/CommonCfg.vcproj b/CommonCfg/CommonCfg.vcproj new file mode 100644 index 0000000..14fdbc4 --- /dev/null +++ b/CommonCfg/CommonCfg.vcproj @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CommonCfg/Config.c b/CommonCfg/Config.c new file mode 100644 index 0000000..60ff9c2 --- /dev/null +++ b/CommonCfg/Config.c @@ -0,0 +1,141 @@ +#include "stdafx.h" + +const Config DEFAULT_CONFIG = { + {0}, // Use default (My Documents/Screenshots) + FALSE, // Don't upload screenshots + TRUE, // Do keep screenshots + FALSE, // Don't start helper on startup + HSC_WIN10 // Emulate Win7+ "Snipping Tool" shortcut +}; + +#define OpenRootKey(hkey,access) \ + RegCreateKeyEx(HKEY_CURRENT_USER, \ + _T(ROOT_KEYNAME), \ + 0, \ + NULL, \ + REG_OPTION_NON_VOLATILE, \ + access, \ + NULL, \ + hkey, \ + NULL) + +// Enumeration callback for GetConfig +static BOOL +ConfigCallback(HKEY hKey, + LPTSTR szValName, + DWORD dwValSize, + DWORD dwValType, + PVOID pData) +{ + Config *cfg = pData; + if (!cfg) return FALSE; + + CopyIfMatch(hKey, cfg, + ScreenshotDir, REG_PATH_SZ) + else CopyIfMatch(hKey, cfg, + Upload, REG_DWORD) + else CopyIfMatch(hKey, cfg, + KeepScreenshots, REG_DWORD) + else CopyIfMatch(hKey, cfg, + HelperStartup, REG_DWORD) + else CopyIfMatch(hKey, cfg, + HelperShortcut, REG_DWORD) + + return TRUE; +} + +Config * +GetConfig(void) +{ + HKEY hkRootKey = NULL; + BOOL bRes; + DWORD dwErr; + HANDLE hHeap = GetProcessHeap(); + Config *cfg = HeapAlloc(hHeap, 0, sizeof(Config)); + + if (!cfg) return NULL; + + // Copy in the default config + CopyMemory(cfg, &DEFAULT_CONFIG, sizeof(Config)); + + if ((dwErr = OpenRootKey(&hkRootKey, KEY_READ))) { + HeapFree(hHeap, 0, cfg); + SetLastError(dwErr); + return NULL; + } + + bRes = RegGetAllValues(hkRootKey, ConfigCallback, cfg); + dwErr = GetLastError(); + RegCloseKey(hkRootKey); + + if (!bRes) { + FreeConfig(cfg); + SetLastError(dwErr); + return NULL; + } + + // If ScreenshotDir is NULL, set it to My Documents + if (!strlen(cfg->ScreenshotDir)) { + HRESULT hrRes; + + // afaict, My Pictures isn't an easily accessible folder. + // There's CSIDL_MYPICTURES, but that's XP-only (and also + // points to a virtual folder???). + hrRes = SHGetFolderPath(NULL, + CSIDL_PERSONAL, + NULL, + SHGFP_TYPE_CURRENT, + cfg->ScreenshotDir); + + if (FAILED(hrRes)) { + // TODO: error management + // For now, just empty the string + *cfg->ScreenshotDir = 0; + } else { + PathAppend(cfg->ScreenshotDir, + _T("Screenshots")); + } + } + + // TODO: Detect startup + + return cfg; +} + +BOOL +SaveConfig(Config *cfg) +{ + HKEY hkRootKey; + DWORD dwErr; + + if (!cfg) return FALSE; + + if ((dwErr = OpenRootKey(&hkRootKey, KEY_WRITE))) { + SetLastError(dwErr); + return FALSE; + } + + StoreConfig(hkRootKey, cfg, + ScreenshotDir, REG_SZ) + StoreConfig(hkRootKey, cfg, + Upload, REG_DWORD) + StoreConfig(hkRootKey, cfg, + KeepScreenshots, REG_DWORD) + StoreConfig(hkRootKey, cfg, + HelperStartup, REG_DWORD) + StoreConfig(hkRootKey, cfg, + HelperShortcut, REG_DWORD) + + RegCloseKey(hkRootKey); + return TRUE; +} + +void +FreeConfig(Config *cfg) +{ + HANDLE hHeap = GetProcessHeap(); + if (!cfg) return; + + HeapFree(hHeap, 0, cfg); + return; +} \ No newline at end of file diff --git a/CommonCfg/Export/Config.h b/CommonCfg/Export/Config.h new file mode 100644 index 0000000..e036eb8 --- /dev/null +++ b/CommonCfg/Export/Config.h @@ -0,0 +1,86 @@ +#pragma once + +typedef struct _MultiString { + LPTSTR * Values; // Array of string values + size_t Count; // Length of Values + size_t TotalLength; // Used by registry helpers. + // NOTE: This length includes the NULL + // terminator characters of each string + DWORD Reserved[2]; // Used internally +} MultiString; + +// Initializes an existing MultiString array, with the given length. +// If dwInitLen is 0, no entries are pre-allocated. +BOOL +MultiString_Init(MultiString *ms, + DWORD dwInitLen); + +// Appends a copy of szNew to the end of the list. +// +// NOTE: A new string with the same length as szNew is allocated and +// stored in ms. This makes future internal freeing easier, but means +// the function caller is responsible for freeing the parameter. +BOOL +MultiString_Append(MultiString *ms, + LPCTSTR szNew); + +// Removes the string at dwIdx, freeing the memory used. +BOOL +MultiString_Remove(MultiString *ms, + DWORD dwIdx); + +// Replaces the string at dwIdx with szNew. Allocations are performed +// similar to MultiString_Append. +BOOL +MultiString_Replace(MultiString *ms, + DWORD dwIdx, + LPCTSTR szNew); + +// Frees all memory in a MultiString structure. +void +MultiString_Dispose(MultiString *ms); + +typedef enum _HelperShortcut { + HSC_MACOS, // Pretend to be like MacOS (Win+Shift+4) + HSC_WIN10, // Pretend to be like Windows (Win+Shift+S) + HSC_CUSTOM // Use a custom keyboard shortcut +} HlprKbd; + +typedef struct _GlobalConfig { + TCHAR ScreenshotDir[MAX_PATH]; + BOOL Upload; + BOOL KeepScreenshots; + BOOL HelperStartup; + HlprKbd HelperShortcut; +} Config; + +typedef enum _AuthMethod { + CFG_NOAUTH, // Anonymous HTTP requests + CFG_BASICAUTH, // Use HTTP Basic auth + CFG_DIGESTAUTH, // Use HTTP Digest auth + CFG_TOKENAUTH // Use a custom Authorization header +} AuthMethod; + +typedef struct _UploadConfig { + LPTSTR Name; + LPTSTR URL; + LPTSTR Method; + /* + * NOTE: If AuthType == CFG_TOKENAUTH, Username is blank/NULL and + * Password is the token. + */ + LPTSTR Username; + LPTSTR Password; + AuthMethod HTTPAuthMethod; + + MultiString Headers; + MultiString Parameters; +} UploadConfig; + + +// Gets the current configuration from the registry. +Config *GetConfig(void); +// Saves the Config object to the registry. +BOOL SaveConfig(Config *cfg); +// Frees all memory associated with the Config object. +void FreeConfig(Config *cfg); \ No newline at end of file diff --git a/CommonCfg/MultiString.c b/CommonCfg/MultiString.c new file mode 100644 index 0000000..431b6a4 --- /dev/null +++ b/CommonCfg/MultiString.c @@ -0,0 +1,196 @@ +#include "stdafx.h" + +#define STRSAFE_NO_DEPRECATE +#include + +#define CurAlloc Reserved[0] + + +BOOL +MultiString_Init(MultiString *ms, + DWORD dwInitLen) +{ + HANDLE hHeap = GetProcessHeap(); + + if (!ms) { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if (dwInitLen) { + ms->CurAlloc = dwInitLen; + ms->Values = HeapAlloc(hHeap, + HEAP_ZERO_MEMORY, + sizeof(LPTSTR) * dwInitLen); + if (!ms->Values) return FALSE; + ms->Count = 0; + } else { + // Otherwise, this amounts to a zero-out + ZeroMemory(ms, sizeof(MultiString)); + } + + return TRUE; +} + +__inline static LPTSTR +CreateStrCopy(LPCTSTR szStr) +{ + HRESULT hrRes; + LPTSTR szNewCopy; + // dwLen needs to include the NULL terminator + size_t dwLen = _tcslen(szStr) + 1; + HANDLE hHeap = GetProcessHeap(); + + // Allocate the new string + szNewCopy = HeapAlloc(hHeap, + HEAP_ZERO_MEMORY, + sizeof(TCHAR) * dwLen); + if (!szNewCopy) return NULL; + + // Copy the actual data over + hrRes = StringCchCopy(szNewCopy, + dwLen, + szStr); + if (FAILED(hrRes)) { + HeapFree(hHeap, 0, szNewCopy); + return NULL; + } + + return szNewCopy; +} + +BOOL +MultiString_Append(MultiString *ms, + LPCTSTR szNew) +{ + LPTSTR szNewCopy = NULL; + DWORD dwNewLen; + HANDLE hHeap = GetProcessHeap(); + + if (!ms || !szNew) { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + dwNewLen = (DWORD)_tcslen(szNew) + 1; + + // Do we need to allocate more room for pointers? + if (ms->Values == NULL) { + // Oops, we have nothing! Let's allocate a couple + if (!MultiString_Init(ms, 3)) { + return FALSE; + } + } else if (ms->CurAlloc == ms->Count) { + DWORD dwNewAlloc = ms->CurAlloc + 3; + LPTSTR *pszNew = HeapReAlloc(hHeap, + 0, + ms->Values, + sizeof(LPTSTR) * dwNewAlloc); + if (!pszNew) { + // ReAlloc failed :< + return FALSE; + } + ms->CurAlloc = dwNewAlloc; + ms->Values = pszNew; + } + + szNewCopy = CreateStrCopy(szNew); + if (!szNewCopy) return FALSE; + + // All set, append to the end and increment Count + ms->Values[ms->Count++] = szNewCopy; + ms->TotalLength += dwNewLen; + + return TRUE; +} + +BOOL +MultiString_Remove(MultiString *ms, + DWORD dwIdx) +{ + DWORD dwLen = 0; + HANDLE hHeap = GetProcessHeap(); + + if (!ms) return FALSE; + + if (dwIdx >= ms->Count) { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } else if (ms->Values[dwIdx] != NULL) { + dwLen = (DWORD)_tcslen(ms->Values[dwIdx]) + 1; + + HeapFree(hHeap, 0, ms->Values[dwIdx]); + } + + + // Shift down the remaining strings + if (dwIdx < ms->Count - 1) { + size_t i; + for (i = dwIdx; i < ms->Count - 1; i++) { + ms->Values[i] = ms->Values[i + 1]; + } + } + + ms->Count--; + ms->TotalLength -= dwLen; + + return TRUE; +} + +BOOL +MultiString_Replace(MultiString *ms, + DWORD dwIdx, + LPCTSTR szNew) +{ + DWORD dwOldLen = 0, + dwNewLen = 0; + LPTSTR szTmp; + HANDLE hHeap = GetProcessHeap(); + + if (!ms || !szNew) return FALSE; + + if (dwIdx >= ms->Count) { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + dwNewLen = (DWORD)_tcslen(szNew) + 1; + + // Create the copied new string + szTmp = CreateStrCopy(szNew); + if (!szTmp) return FALSE; + + // Perform the switcheroo + if (ms->Values[dwIdx] != NULL) { + dwOldLen = (DWORD)_tcslen(ms->Values[dwIdx]) + 1; + + HeapFree(hHeap, 0, ms->Values[dwIdx]); + } + + ms->Values[dwIdx] = szTmp; + ms->TotalLength = ms->TotalLength + dwNewLen - dwOldLen; + + return TRUE; +} + +void +MultiString_Dispose(MultiString *ms) +{ + HANDLE hHeap = GetProcessHeap(); + DWORD i; + + if (!ms) return; + + if (ms->Values != NULL) { + for (i = 0; i < ms->Count; i++) { + HeapFree(hHeap, 0, ms->Values[i]); + } + + HeapFree(hHeap, 0, ms->Values); + ms->Values = NULL; + } + ms->Count = 0; + ms->CurAlloc = 0; + + return; +} \ No newline at end of file diff --git a/CommonCfg/ReadMe.txt b/CommonCfg/ReadMe.txt new file mode 100644 index 0000000..4cc1b4c --- /dev/null +++ b/CommonCfg/ReadMe.txt @@ -0,0 +1,29 @@ +======================================================================== + STATIC LIBRARY : CommonCfg Project Overview +======================================================================== + +AppWizard has created this CommonCfg library project for you. +This file contains a summary of what you will find in each of the files that +make up your CommonCfg application. + + +CommonCfg.vcproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + + +///////////////////////////////////////////////////////////////////////////// + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named CommonCfg.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/CommonCfg/RegHelper.c b/CommonCfg/RegHelper.c new file mode 100644 index 0000000..7047020 --- /dev/null +++ b/CommonCfg/RegHelper.c @@ -0,0 +1,280 @@ +#include "stdafx.h" + +#define STRSAFE_NO_DEPRECATE +#include + +// Used by CopyRegValue specifically for copying +// REG_MULTI_SZ values, because those in particular are +// really funky. +static BOOL +CopyMultiValue(HKEY hKey, + PVOID pVal, + LPCTSTR szName, + DWORD dwSize) +{ + int iStrCount = 0; + LPTSTR szMulti = NULL, + ptr; + BOOL bWasNull = FALSE; + LONG lRes; + MultiString *msData = (MultiString *)pVal; + + HANDLE hHeap = GetProcessHeap(); + + szMulti = HeapAlloc(hHeap, + HEAP_ZERO_MEMORY, + dwSize + sizeof(TCHAR)); + if (!szMulti) return FALSE; + + // Get the multi-string value + if (!(lRes = RegQueryValueEx(hKey, + szName, + 0, + NULL, + szMulti, + &dwSize))) + { + HeapFree(hHeap, 0, szMulti); + SetLastError(lRes); + return FALSE; + } + + for (ptr = szMulti; (ptr - szMulti) < (LONG)dwSize; ptr++) { + if (*ptr == 0) { + if (bWasNull) { + // Two NULLs in a row, we're done! + break; + } else { + // End of string, add to the list + bWasNull = TRUE; + iStrCount++; + } + } + bWasNull = FALSE; + } + + if (!bWasNull) { + // Not properly NULL terminated, sigh... + // Make sure to count the last string. + iStrCount++; + } + + if (!MultiString_Init(msData, iStrCount)) { + HeapFree(hHeap, 0, szMulti); + return FALSE; + } else { + int i; + for (i = 0, ptr = szMulti; i < iStrCount; i++, ptr += strlen(ptr) + 1) { + if (!MultiString_Append(msData, ptr)) { + DWORD err = GetLastError(); + + HeapFree(hHeap, 0, szMulti); + MultiString_Dispose(msData); + + SetLastError(err); + return FALSE; + } + } + } + + HeapFree(hHeap, 0, szMulti); + + return TRUE; +} + +BOOL +CopyRegValue(HKEY hKey, + PVOID pVal, + LPCTSTR szName, + DWORD dwType, + DWORD dwSize) +{ + HANDLE hHeap = GetProcessHeap(); + PVOID pData = pVal; + LONG lRes; + + if (pVal == NULL || + szName == NULL || + hKey == NULL) { + return FALSE; + } + + if (dwType == REG_BINARY || + dwType == REG_SZ) { + pData = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, dwSize); + if (!pData) return FALSE; + *(PVOID *)pVal = pData; + } else if (dwType == REG_MULTI_SZ) { + // REG_MULTI_SZ is complex enough to warrant its own + // function + return CopyMultiValue(hKey, pVal, szName, dwSize); + } + + if ((lRes = RegQueryValueEx(hKey, + szName, + NULL, + NULL, + pData, + &dwSize)) != ERROR_SUCCESS) + { + SetLastError(lRes); + return FALSE; + } + + return TRUE; +} + +// Enumerates across all values in a key, running the given +// RegEnumCallback. Use `pData` to pass application-defined +// data to your enumerator callback. +BOOL RegGetAllValues(HKEY hKey, + RegEnumCallback cb, + PVOID pData) +{ + LONG lRes; + TCHAR szValName[512]; + DWORD dwNameLen = 512, + dwIdx, + dwValType, + dwValSize; + if (!hKey || !cb) return FALSE; + + for (dwIdx = 0; + dwNameLen = 512, // reset dwNameLen first + !(lRes = RegEnumValue(hKey, + dwIdx, + szValName, + &dwNameLen, + 0, + &dwValType, + NULL, + &dwValSize)); + dwIdx++) { + // Enforce null termination + szValName[min(511, dwNameLen)] = 0; + // pass to callback + if (!cb(hKey, + szValName, + dwValSize, + dwValType, + pData)) { + // Callback returned failure, bail out + return FALSE; + } + } + + if (lRes != ERROR_NO_MORE_ITEMS) { + // Some enumeration error happened, store it + // and report the failure + SetLastError(lRes); + return FALSE; + } + + return TRUE; +} + +BOOL +RegStoreMultiString(HKEY hKey, + LPTSTR szValName, + MultiString *msData) +{ + LPTSTR szMulti; + DWORD dwMultiSize; + LONG lRes; + HANDLE hHeap = GetProcessHeap(); + + if (!msData->TotalLength) return FALSE; + + // Total size + second NULL terminator + dwMultiSize = (DWORD)msData->TotalLength + 1; + szMulti = HeapAlloc(hHeap, + HEAP_ZERO_MEMORY, + dwMultiSize * sizeof(TCHAR)); + if (!szMulti) return FALSE; + + // Copy all the strings into one buffer, as per REG_MULTI_SZ + { + DWORD i, dwCurLen; + for (i = 0, dwCurLen = 0; + i < msData->Count && dwCurLen < dwMultiSize; + dwCurLen += (DWORD)_tcslen(msData->Values[i++]) + 1) { + HRESULT hrRes = StringCchCopy(&szMulti[dwCurLen], + _tcslen(msData->Values[i]) + 1, + msData->Values[i]); + if (FAILED(hrRes)) { + HeapFree(hHeap, 0, szMulti); + return FALSE; + } + } + } + + lRes = RegSetValueEx(hKey, + szValName, + 0, + REG_MULTI_SZ, + szMulti, + dwMultiSize); + + HeapFree(hHeap, 0, szMulti); + + if (lRes != ERROR_SUCCESS) { + SetLastError(lRes); + return FALSE; + } + + return TRUE; +} + +// Stores a value in the registry, making assumptions based on the +// key type. +BOOL +RegStoreValue(HKEY hKey, + LPTSTR szValName, + PVOID pData, + DWORD dwValType) +{ + DWORD dwStoreSize; + LONG lRes; + if (!hKey || !szValName || !pData) return FALSE; + + switch (dwValType) { + case REG_SZ: + case REG_EXPAND_SZ: + dwStoreSize = (DWORD)_tcslen((LPTSTR)pData) + sizeof(TCHAR); + break; + case REG_DWORD: + dwStoreSize = sizeof(DWORD); + break; +#ifdef QWORD + case REG_QWORD: + dwStoreSize = sizeof(QWORD); + break; +#endif + case REG_BINARY: + // TODO: handle REG_BINARY? will we ever use it? + OutputDebugString(_T("REG_BINARY attempted to be stored! Unsupported")); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + case REG_MULTI_SZ: + return RegStoreMultiString(hKey, + szValName, + (MultiString *)pData); + default: + // ignore + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + lRes = RegSetValueEx(hKey, + szValName, + 0, + dwValType, + pData, + dwStoreSize); + if (lRes != ERROR_SUCCESS) { + SetLastError(lRes); + return FALSE; + } + + return TRUE; +} \ No newline at end of file diff --git a/CommonCfg/RegHelper.h b/CommonCfg/RegHelper.h new file mode 100644 index 0000000..415fad0 --- /dev/null +++ b/CommonCfg/RegHelper.h @@ -0,0 +1,68 @@ +// Helper functions for handling the registry +#pragma once + +// Used behind CopyIfMatch. Copies a given registry value to +// another location, regardless of type. +BOOL CopyRegValue(HKEY hKey, + PVOID pVal, + LPCTSTR szName, + DWORD dwType, + DWORD dwSize); + +// Callback for RegGetAllValues +typedef BOOL (*RegEnumCallback)(HKEY hKey, + LPTSTR szValName, + DWORD dwValSize, + DWORD dwValType, + PVOID pData); + +// Enumerates across all values in a key, running the given +// RegEnumCallback. Use `pData` to pass application-defined +// data to your enumerator callback. +BOOL RegGetAllValues(HKEY hKey, + RegEnumCallback cb, + PVOID pData); + +// Wrapper function for storing reg values, regardless of type +BOOL RegStoreValue(HKEY hKey, + LPTSTR szValName, + PVOID pData, + DWORD dwValType); + +// Used primarily by RegEnumCallback, hence the hardcoded +// var names. +// You might want to check out one of the Get*Config functions +// for more background on how to use this, but it should make the +// callback function as easy as: +// +// CopyIfMatch(hKey, myStruct, +// myField, REG_SZ) // or any type +// else CopyIfMatch(hKey, myStruct, +// myOtherField, REG_DWORD) // also note: no semicolons +// // ... and so on +#define CopyIfMatch(hk,x,name,type) \ + if (!_tcsncmp(_T(#name), \ + szValName, \ + _tcslen(_T(#name))) \ + && (dwValType & 0xfff) == type) { \ + if (!CopyRegValue(hk, \ + &((x)->name), \ + _T(#name), \ + type, \ + dwValSize)) { \ + return FALSE; \ + } \ + } + +// Helper macro for Save*Config functions. Assumes a BOOL-returning +// function in a vacuum. +#define StoreConfig(hk,x,name,type) \ + if (!RegStoreValue(hk, _T(#name), &(x)->name, type)) { \ + DWORD dwRegErr = GetLastError(); \ + RegCloseKey(hk); \ + return FALSE; \ + } + +#define ROOT_KEYNAME "SOFTWARE\\Grabby" +// Hack to handle REG_SZ values that are actually TCHAR[MAX_PATH]. +#define REG_PATH_SZ (0xf000 & REG_SZ) \ No newline at end of file diff --git a/CommonCfg/UploadConfig.c b/CommonCfg/UploadConfig.c new file mode 100644 index 0000000..a21da53 --- /dev/null +++ b/CommonCfg/UploadConfig.c @@ -0,0 +1,65 @@ +#include "stdafx.h" + +static BOOL +UploadConfigCb(HKEY hKey, + LPTSTR szValName, + DWORD dwValSize, + DWORD dwValType, + PVOID pData) +{ + UploadConfig *ucfg = pData; + if (!ucfg) return FALSE; + + CopyIfMatch(hKey, ucfg, + Name, REG_SZ) + else CopyIfMatch(hKey, ucfg, + URL, REG_SZ) + else CopyIfMatch(hKey, ucfg, + Method, REG_SZ) + else CopyIfMatch(hKey, ucfg, + Username, REG_SZ) + else CopyIfMatch(hKey, ucfg, + Password, REG_SZ) + else CopyIfMatch(hKey, ucfg, + HTTPAuthMethod, REG_DWORD) + else CopyIfMatch(hKey, ucfg, + Headers, REG_MULTI_SZ) + else CopyIfMatch(hKey, ucfg, + Parameters, REG_MULTI_SZ) + + return TRUE; +} + +UploadConfig * +GetUploadConfig(void) +{ + + HKEY hkUploadKey = NULL; + HANDLE hHeap = GetProcessHeap(); + LONG lRes; + UploadConfig *ucfg = HeapAlloc(hHeap, + HEAP_ZERO_MEMORY, + sizeof(UploadConfig)); + + if (!ucfg) return NULL; + + // TODO: defaults? + lRes = RegOpenKeyEx(HKEY_CURRENT_USER, + _T(ROOT_KEYNAME "\\Upload"), + 0, + KEY_READ, + &hkUploadKey); + + if (lRes) { + SetLastError(lRes); + HeapFree(hHeap, 0, ucfg); + return NULL; + } + + if (!RegGetAllValues(hkUploadKey, UploadConfigCb, ucfg)) { + HeapFree(hHeap, 0, ucfg); + return NULL; + } + + return ucfg; +} \ No newline at end of file diff --git a/CommonCfg/stdafx.c b/CommonCfg/stdafx.c new file mode 100644 index 0000000..1577c4e --- /dev/null +++ b/CommonCfg/stdafx.c @@ -0,0 +1 @@ +#include "stdafx.h" \ No newline at end of file diff --git a/CommonCfg/stdafx.h b/CommonCfg/stdafx.h new file mode 100644 index 0000000..adcd07b --- /dev/null +++ b/CommonCfg/stdafx.h @@ -0,0 +1,17 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#include +#include +#include +#include + +// TODO: reference additional headers your program requires here +#include "RegHelper.h" +#include "Export/Config.h" \ No newline at end of file diff --git a/Grabby.sln b/Grabby.sln index 3e07894..b606201 100644 --- a/Grabby.sln +++ b/Grabby.sln @@ -1,5 +1,13 @@ Microsoft Visual Studio Solution File, Format Version 8.00 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Grabby", "Grabby.vcproj", "{700E5DD7-DD00-46D9-92DF-2C132940F53D}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Grabby", "Grabby\Grabby.vcproj", "{700E5DD7-DD00-46D9-92DF-2C132940F53D}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GrbyCfg", "GrbyCfg\GrbyCfg.vcproj", "{BBB3E933-71CC-4EF7-BF56-52371B9AF938}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CommonCfg", "CommonCfg\CommonCfg.vcproj", "{61829180-05C1-4865-B9C3-FEB983AD6997}" ProjectSection(ProjectDependencies) = postProject EndProjectSection EndProject @@ -13,6 +21,14 @@ Global {700E5DD7-DD00-46D9-92DF-2C132940F53D}.Debug.Build.0 = Debug|Win32 {700E5DD7-DD00-46D9-92DF-2C132940F53D}.Release.ActiveCfg = Release|Win32 {700E5DD7-DD00-46D9-92DF-2C132940F53D}.Release.Build.0 = Release|Win32 + {BBB3E933-71CC-4EF7-BF56-52371B9AF938}.Debug.ActiveCfg = Debug|Win32 + {BBB3E933-71CC-4EF7-BF56-52371B9AF938}.Debug.Build.0 = Debug|Win32 + {BBB3E933-71CC-4EF7-BF56-52371B9AF938}.Release.ActiveCfg = Release|Win32 + {BBB3E933-71CC-4EF7-BF56-52371B9AF938}.Release.Build.0 = Release|Win32 + {61829180-05C1-4865-B9C3-FEB983AD6997}.Debug.ActiveCfg = Debug|Win32 + {61829180-05C1-4865-B9C3-FEB983AD6997}.Debug.Build.0 = Debug|Win32 + {61829180-05C1-4865-B9C3-FEB983AD6997}.Release.ActiveCfg = Release|Win32 + {61829180-05C1-4865-B9C3-FEB983AD6997}.Release.Build.0 = Release|Win32 EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution EndGlobalSection diff --git a/Bitmap.c b/Grabby/Bitmap.c similarity index 100% rename from Bitmap.c rename to Grabby/Bitmap.c diff --git a/Bitmap.h b/Grabby/Bitmap.h similarity index 100% rename from Bitmap.h rename to Grabby/Bitmap.h diff --git a/Grabby.c b/Grabby/Grabby.c similarity index 80% rename from Grabby.c rename to Grabby/Grabby.c index d44d8b9..c533d22 100644 --- a/Grabby.c +++ b/Grabby/Grabby.c @@ -5,6 +5,7 @@ #include "Bitmap.h" #include "Overlay.h" #include "Grabby.h" +#include /** * Print an error with a given context before bailing out @@ -43,7 +44,7 @@ void Die(LPTSTR lpContextMsg) _T("Catastrophic Error"), MB_OK | MB_ICONERROR); } else { - _sntprintf(lpszFullMsg, iFullLen, lpszFmt, lpContextMsg, lpszError); + StringCchPrintf(lpszFullMsg, iFullLen, lpszFmt, lpContextMsg, lpszError); MessageBox(NULL, lpszFullMsg, _T("Fatal Error"), @@ -62,16 +63,24 @@ int APIENTRY _tWinMain(HINSTANCE hInstance, SYSTEMTIME stNow = {0}; RECT capRegion = {0}; Screen *scrn = CreateScreen(); -#define OUTPUT_DIR_LEN 96 - TCHAR szOutputDir[OUTPUT_DIR_LEN + 1]; -#define FULL_OUT_PATH_LEN OUTPUT_DIR_LEN + 24 - TCHAR szFullOutPath[FULL_OUT_PATH_LEN + 1]; + Config *cfg; + TCHAR szFullOutPath[MAX_PATH]; HRESULT hRes; if (!scrn) { Die("Couldn't get screen information"); } + cfg = GetConfig(); + if (!cfg) { + Die("Couldn't get config"); + } + + if (!PathFileExists(cfg->ScreenshotDir) && + !CreateDirectory(cfg->ScreenshotDir, NULL)) { + Die("Couldn't create configured folder"); + } + switch (CreateOverlay(hInstance, scrn)) { case -1: Die("Couldn't create overlay window"); @@ -86,16 +95,12 @@ int APIENTRY _tWinMain(HINSTANCE hInstance, GetChosenRect(&capRegion); - if (!GetTempPath(OUTPUT_DIR_LEN, szOutputDir)) { - Die("Couldn't get temporary directory"); - } - GetLocalTime(&stNow); hRes = StringCchPrintf(szFullOutPath, - FULL_OUT_PATH_LEN, - "%s\\scrn_%04d%02d%02d_%02d%02d%02d.bmp", - szOutputDir, + MAX_PATH, + "%s\\Screenshot at %04d-%02d-%02d %02d.%02d.%02d.bmp", + cfg->ScreenshotDir, stNow.wYear, stNow.wMonth, stNow.wDay, diff --git a/Grabby.h b/Grabby/Grabby.h similarity index 100% rename from Grabby.h rename to Grabby/Grabby.h diff --git a/Grabby.ico b/Grabby/Grabby.ico similarity index 100% rename from Grabby.ico rename to Grabby/Grabby.ico diff --git a/Grabby.rc b/Grabby/Grabby.rc similarity index 55% rename from Grabby.rc rename to Grabby/Grabby.rc index 57d852f..d973872 100644 --- a/Grabby.rc +++ b/Grabby/Grabby.rc @@ -1,4 +1,4 @@ -//Microsoft Visual C++ generated resource script. +// Microsoft Visual C++ generated resource script. // #include "resource.h" @@ -10,12 +10,58 @@ #define APSTUDIO_HIDDEN_SYMBOLS #include "windows.h" #undef APSTUDIO_HIDDEN_SYMBOLS + ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// Japanese resources + #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN) -LANGUAGE 17, 1 +#ifdef _WIN32 +LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT #pragma code_page(932) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Japanese resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 ///////////////////////////////////////////////////////////////////////////// // @@ -24,95 +70,10 @@ LANGUAGE 17, 1 // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. - -IDI_GRABBY ICON "Grabby.ico" -IDI_SMALL ICON "small.ico" - -///////////////////////////////////////////////////////////////////////////// -// -// Menu -// - -IDC_GRABBY MENU -BEGIN - POPUP "&File" - BEGIN - MENUITEM "E&xit", IDM_EXIT - END - POPUP "&Help" - BEGIN - MENUITEM "&About ...", IDM_ABOUT - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Accelerator -// - -IDC_GRABBY ACCELERATORS -BEGIN - "?", IDM_ABOUT, ASCII, ALT - "/", IDM_ABOUT, ASCII, ALT -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_ABOUTBOX DIALOG 22, 17, 230, 75 -STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU -CAPTION "About" -FONT 9, "System" -BEGIN - ICON IDI_GRABBY,IDC_MYICON,14,9,16,16 - LTEXT "Grabby Version 1.0",IDC_STATIC,49,10,119,8,SS_NOPREFIX - LTEXT "Copyright (C) 2024",IDC_STATIC,49,20,119,8 - DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP -END - - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" - "#include ""windows.h""\r\n" - "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - -///////////////////////////////////////////////////////////////////////////// -// -// String Table -// - -STRINGTABLE -BEGIN - IDC_GRABBY "GRABBY" - IDS_APP_TITLE "Grabby" -END - -#endif +IDI_GRABBY ICON "Grabby.ico" +IDI_SMALL ICON "small.ico" +IDI_ICON1 ICON "icon1.ico" +#endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// @@ -126,3 +87,4 @@ END ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED + diff --git a/Grabby.vcproj b/Grabby/Grabby.vcproj similarity index 82% rename from Grabby.vcproj rename to Grabby/Grabby.vcproj index be2f988..a1ba43c 100644 --- a/Grabby.vcproj +++ b/Grabby/Grabby.vcproj @@ -19,6 +19,7 @@ + DebugInformationFormat="3" + CompileAs="1"/> + + + + RelativePath="..\ReadMe.txt"> diff --git a/Overlay.c b/Grabby/Overlay.c similarity index 100% rename from Overlay.c rename to Grabby/Overlay.c diff --git a/Overlay.h b/Grabby/Overlay.h similarity index 100% rename from Overlay.h rename to Grabby/Overlay.h diff --git a/Grabby/icon1.ico b/Grabby/icon1.ico new file mode 100644 index 0000000..c2746b0 Binary files /dev/null and b/Grabby/icon1.ico differ diff --git a/Grabby/resource.h b/Grabby/resource.h new file mode 100644 index 0000000..da08ba3 --- /dev/null +++ b/Grabby/resource.h @@ -0,0 +1,36 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Grabby.rc +// +#define IDC_MYICON 2 +#define IDD_GRABBY_DIALOG 102 +#define IDS_APP_TITLE 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDI_GRABBY 107 +#define IDI_SMALL 108 +#define IDC_GRABBY 109 +#define IDR_MAINFRAME 128 +#define IDI_ICON1 129 +#define IDC_TABVIEW 1000 +#define IDC_APPLY 1001 +#define IDC_EDIT1 1004 +#define IDC_BUTTON1 1005 +#define IDC_CHECK1 1006 +#define IDC_URLINPUT 1007 +#define IDC_ENCODEFILE 1008 +#define IDC_COMBO1 1009 +#define IDC_UPLOADMETHOD 1009 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 132 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1010 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/small.ico b/Grabby/small.ico similarity index 100% rename from small.ico rename to Grabby/small.ico diff --git a/stdafx.c b/Grabby/stdafx.c similarity index 100% rename from stdafx.c rename to Grabby/stdafx.c diff --git a/stdafx.h b/Grabby/stdafx.h similarity index 87% rename from stdafx.h rename to Grabby/stdafx.h index 6319016..e73048a 100644 --- a/stdafx.h +++ b/Grabby/stdafx.h @@ -9,6 +9,7 @@ #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers // Windows Header Files: #include +#include // C RunTime Header Files #include #include @@ -16,4 +17,4 @@ #include #include -#include +#include \ No newline at end of file diff --git a/GrbyCfg/Dialog.c b/GrbyCfg/Dialog.c new file mode 100644 index 0000000..8d6feed --- /dev/null +++ b/GrbyCfg/Dialog.c @@ -0,0 +1,11 @@ +#define _IS_DIALOG_C_ +#include "stdafx.h" +#include "Dialog.h" + + +void +InitUserMsgs(void) +{ + DEFINE_USER_MSG(WMU_SAVE_CONFIG); + DEFINE_USER_MSG(WMU_SETTING_CHANGED); +} \ No newline at end of file diff --git a/GrbyCfg/Dialog.h b/GrbyCfg/Dialog.h new file mode 100644 index 0000000..41e3cfc --- /dev/null +++ b/GrbyCfg/Dialog.h @@ -0,0 +1,25 @@ +#pragma once + +#define _DLG_MSG_SUFFIX "{990f4b34-579a-4fbc-a5b2-08f46ef5e9c5}" +#define USER_MSG_NAME(name) _T( #name _DLG_MSG_SUFFIX ) +#define DEFINE_USER_MSG(name) {\ + LPCTSTR name##_MSG = USER_MSG_NAME(name); \ + name = RegisterWindowMessage(name##_MSG); \ +} +#define DECLARE_USER_MSG(name) \ + UINT name; + +/* --- */ + +DECLARE_USER_MSG(WMU_SAVE_CONFIG) +DECLARE_USER_MSG(WMU_SETTING_CHANGED) + +void InitUserMsgs(void); + +#define ChangedSetting(hwnd) \ + SendMessage(hwnd, \ + WMU_SETTING_CHANGED, \ + 0, 0) + +#define IsChecked(ref) \ + (IsDlgButtonChecked(hDlg, ref) == BST_CHECKED) \ No newline at end of file diff --git a/GrbyCfg/General.h b/GrbyCfg/General.h new file mode 100644 index 0000000..c6d3a7b --- /dev/null +++ b/GrbyCfg/General.h @@ -0,0 +1,4 @@ +#pragma once + +// Define the dialog proc +DEF_DLGPROC(GeneralTab); \ No newline at end of file diff --git a/GrbyCfg/GeneralCfg.c b/GrbyCfg/GeneralCfg.c new file mode 100644 index 0000000..cd07ba1 --- /dev/null +++ b/GrbyCfg/GeneralCfg.c @@ -0,0 +1,182 @@ +/* General configuration dialog */ + +#include "stdafx.h" +#include "GrbyCfg.h" + +int CALLBACK +ScreenshotBrowseCallback(HWND hWnd, + UINT uMsg, + LPARAM lParam, + LPARAM lpData) +{ + if (uMsg == BFFM_INITIALIZED && lpData) { + // Set root dir + SendMessage(hWnd, + BFFM_SETSELECTION, + 1, + lpData); + } + + return 0; +} + +INT_PTR CALLBACK +GeneralTab_DlgProc(HWND hDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + static Config *cfg = NULL; + static HWND hStoreDir = NULL, + hCustomShortcut = NULL, + hParentDlg = NULL; + + switch (uMsg) { + case WM_INITDIALOG: + hParentDlg = GetParent(hDlg); + + cfg = GetConfig(); + if (!cfg) { + // TODO: better error management + // (probably SendMessage upstream) + MessageBox(hDlg, + _T("Can't get config! (TODO: error msg)"), + _T("Config Error"), + MB_OK | MB_ICONERROR); + DestroyWindow(hDlg); + } + + hStoreDir = GetDlgItem(hDlg, IDC_SCRNDIR); + if (!hStoreDir) { + // TODO: error management x_x + return FALSE; + } + + if (strlen(cfg->ScreenshotDir) > 0) { + // Set the screenshot dir, if we have it + SendMessage(hStoreDir, + EM_SETLIMITTEXT, + (WPARAM)MAX_PATH, + 0); + SetWindowText(hStoreDir, + cfg->ScreenshotDir); + } + + if (cfg->KeepScreenshots) { + // Keep screenshots after upload? + CheckDlgButton(hDlg, + IDC_KEEPIMGS, + BST_CHECKED); + } + + if (cfg->HelperStartup) { + // Launch helper on startup? + CheckDlgButton(hDlg, + IDC_LAUNCHHELPER, + BST_CHECKED); + } + + hCustomShortcut = GetDlgItem(hDlg, + IDC_CUSTOMKEY); + if (!hCustomShortcut) { + // TODO: error management orz + return FALSE; + } + + // Enable the shortcut input if Custom is set + EnableWindow(hCustomShortcut, + (cfg->HelperShortcut == HSC_CUSTOM)); + + // Also, check the correct radio box. This is hacky and + // expects IDC_RDMAC/WIN/CUSTOM is not changed, so uhhhh + // please don't change that assumption? ^_^;; + CheckDlgButton(hDlg, + IDC_RDMAC + min(2, cfg->HelperShortcut), + BST_CHECKED); + break; + case WM_NOTIFY: + // TODO + break; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_LAUNCHHELPER: + cfg->HelperStartup = + (IsDlgButtonChecked(hDlg, IDC_LAUNCHHELPER) == BST_CHECKED); + ChangedSetting(hParentDlg); + break; + + case IDC_KEEPIMGS: + cfg->KeepScreenshots = + (IsDlgButtonChecked(hDlg, IDC_KEEPIMGS) == BST_CHECKED); + ChangedSetting(hParentDlg); + break; + + case IDC_RDMAC: + case IDC_RDWIN: + case IDC_RDCUSTOM: + // Generates the correct enum val a la GetConfig. + cfg->HelperShortcut = IsChecked(IDC_RDMAC) ? HSC_MACOS : + (IsChecked(IDC_RDWIN) ? HSC_WIN10 : HSC_CUSTOM); + // Enable/Disable the Custom Shortcut control as needed + EnableWindow(hCustomShortcut, + (cfg->HelperShortcut == HSC_CUSTOM)); + ChangedSetting(hParentDlg); + break; + + case IDC_SCRDIRBROWSE: { + LPITEMIDLIST lPidl; + BROWSEINFO bi = {0}; + + bi.hwndOwner = hDlg; + bi.lParam = (LPARAM)cfg->ScreenshotDir; + bi.lpfn = ScreenshotBrowseCallback; + bi.lpszTitle = _T("Select Screenshot Folder"); + + lPidl = SHBrowseForFolder(&bi); + if (lPidl == NULL) { + // Cancelled + break; + } + + SHGetPathFromIDList(lPidl, + cfg->ScreenshotDir); + + // We don't need the PIDL anymore, so free it + { + LPMALLOC pMalloc; + if (SHGetMalloc(&pMalloc) == NOERROR) { + IMalloc_Free(pMalloc, lPidl); + IMalloc_Release(pMalloc); + } + } + + // Update the edit control + SetWindowText(hStoreDir, + cfg->ScreenshotDir); + + ChangedSetting(hParentDlg); + } + break; + + default: + return FALSE; + } + + break; + default: + // User-defined messages + if (uMsg == WMU_SAVE_CONFIG) { + GetWindowText(hStoreDir, + cfg->ScreenshotDir, + MAX_PATH - 1); + if (!SaveConfig(cfg)) { + // TODO: error management + OutputDebugString(_T("no save x_x")); + } + } else { + // Not a message we care about + return FALSE; + } + } + return TRUE; +} \ No newline at end of file diff --git a/GrbyCfg/GrbyCfg.c b/GrbyCfg/GrbyCfg.c new file mode 100644 index 0000000..e4149a4 --- /dev/null +++ b/GrbyCfg/GrbyCfg.c @@ -0,0 +1,272 @@ +#include "stdafx.h" +#include "GrbyCfg.h" +#define MAX_LOADSTRING 100 +#include + +INT_PTR CALLBACK ConfigDlgProc(HWND, UINT, WPARAM, LPARAM); + +/** + * Print an error with a given context before bailing out + */ +void Die(LPTSTR lpContextMsg) +{ + LPCTSTR lpszFmt = "%s: %s"; + int iFullLen; + LPTSTR lpszError = NULL, + lpszFullMsg = NULL; + + // The whole point of this is to print an actual message, so get the + // actual message + if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + 0, + (LPTSTR) &lpszError, + 0, + NULL)) + { + if (lpszError != NULL) LocalFree(lpszError); + lpszError = _T("unknown error"); + } + + // Create our WMU_* messages + InitUserMsgs(); + + iFullLen = _sctprintf(lpszFmt, lpContextMsg, lpszError); + lpszFullMsg = (LPTSTR) LocalAlloc(LPTR, sizeof(TCHAR) * (iFullLen + 1)); + + if (lpszFullMsg == NULL) { + // I'm honestly gonna guess if we get here, we're out of memory. + // So let's print what we have and bail + MessageBox(NULL, + lpContextMsg, + _T("Catastrophic Error"), + MB_OK | MB_ICONERROR); + } else { + _sntprintf(lpszFullMsg, iFullLen, lpszFmt, lpContextMsg, lpszError); + MessageBox(NULL, + lpszFullMsg, + _T("Fatal Error"), + MB_OK | MB_ICONERROR); + } + + ExitProcess(1); +} + +HINSTANCE g_hInstance = NULL; + +int APIENTRY _tWinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, + int nCmdShow) +{ + BOOL bRet; + MSG msg; + HWND hDlg; + INITCOMMONCONTROLSEX lpInitCtrls = {0}; + HRESULT hrRet; + + g_hInstance = hInstance; + + // Required for tab control + lpInitCtrls.dwSize = sizeof(INITCOMMONCONTROLSEX); + lpInitCtrls.dwICC = ICC_WIN95_CLASSES; + if (!InitCommonControlsEx(&lpInitCtrls)) { + MessageBox(NULL, + _T("Couldn't initialize comctl32! Not sure why..."), + _T("Ooh, rare bug!"), + MB_OK | MB_ICONERROR); + return 1; + } + + // Required for folder browse dialog + hrRet = CoInitializeEx(NULL, + COINIT_APARTMENTTHREADED); + if (hrRet != S_OK) { + MessageBox(NULL, + _T("Couldn't initialize COM!?"), + _T("dot com bust u_u"), + MB_OK | MB_ICONERROR); + return 1; + } + + hDlg = CreateDialog(hInstance, + MAKEINTRESOURCE(IDD_CONFIGDIALOG), + NULL, + ConfigDlgProc); + + if (!hDlg) { + Die("CreateDialog failed"); + } + + ShowWindow(hDlg, nCmdShow); + + while ((bRet = GetMessage(&msg, hDlg, 0, 0)) != FALSE) { + if (bRet == -1) { + // TODO: error handling + break; + } else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + return (int)msg.wParam; +} + +// Helper macro to create a new text (i.e., no icon) tab for config +// items. +// +// NOTE: This is written to use Tab_DlgProc for dlgProc. This +// assumes that tabs will always be valid (enough) for a function +// identifier. +#define NEW_TEXTTAB(name, res) {{ \ + TCIF_TEXT, \ + 0, 0, \ + _T(#name), \ + sizeof(_T(#name)) / sizeof(TCHAR), \ + 0, 0 \ + }, \ + res, \ + name##Tab_DlgProc, \ + NULL \ +} + +INT_PTR CALLBACK +ConfigDlgProc(HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + // Define the tab metadata + static struct { + TCITEM tciTab; + int child; + DLGPROC dlgProc; + HWND hWnd; + } tabs[] = { + NEW_TEXTTAB(General, + IDD_GENCONFIG), + NEW_TEXTTAB(Upload, + IDD_UPLOADCONFIG) + }; + BOOL bClose = FALSE; + + const int tabCount = (sizeof(tabs) / sizeof(tabs[0])); + + switch (uMsg) { + case WM_INITDIALOG: { + RECT rcTab; + int i; + HWND hTabCtrl = GetDlgItem(hWnd, IDC_TABVIEW); + + if (!hTabCtrl) { + Die("Couldn't get tab control"); + } + + for (i = 0; i < tabCount; i++) { + int iResult = TabCtrl_InsertItem( + hTabCtrl, + i, + &tabs[i].tciTab); + if (iResult == -1) { + Die("Couldn't create tabs"); + } + + // Attach the child dialog + tabs[i].hWnd = CreateDialog(g_hInstance, + MAKEINTRESOURCE(tabs[i].child), + hWnd, + tabs[i].dlgProc); + if (!tabs[i].hWnd) { + Die("Couldn't create child tab"); + } + } + + // This has to happen after the tabs are inserted, + // or else we get the wrong client area (tabs don't + // exist yet!) + GetWindowRect(hTabCtrl, &rcTab); + MapWindowPoints(NULL, hWnd, (LPPOINT)&rcTab, 2); + TabCtrl_AdjustRect(hTabCtrl, FALSE, &rcTab); + + for (i = 0; i < tabCount; i++) { + SetWindowPos(tabs[i].hWnd, + HWND_TOP, + rcTab.left, + rcTab.top, + rcTab.right - rcTab.left, + rcTab.bottom - rcTab.top, + (i == 0) ? SWP_SHOWWINDOW : 0); + } + } + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDCANCEL: + DestroyWindow(hWnd); + break; + case IDOK: + bClose = TRUE; + /* PASSTHRU */ + case IDC_APPLY: { + int i; + for (i = 0; i < tabCount; i++) { + SendMessage(tabs[i].hWnd, + WMU_SAVE_CONFIG, + 0, 0); + } + + EnableWindow(GetDlgItem(hWnd, IDC_APPLY), FALSE); + // Handle OK button (Apply + Close) + if (bClose) SendMessage(hWnd, + WM_CLOSE, + 0, + 0); + } + break; + default: + // TODO: other buttons + break; + } + break; + + case WM_CLOSE: + DestroyWindow(hWnd); + break; + + case WM_NOTIFY: + { + LPNMHDR pnmh = (LPNMHDR)lParam; + if (pnmh->code == TCN_SELCHANGE) { + int idx = TabCtrl_GetCurSel(pnmh->hwndFrom); + if (idx < tabCount) { + int i; + for (i = 0; i < tabCount; i++) { + if (tabs[i].hWnd) { + // If it's the child we want, show it. + // If not, hide it. + ShowWindow(tabs[i].hWnd, + (i == idx) ? SW_SHOW : SW_HIDE); + } + } + } + // If idx >= tabCount, ignore the message + } + } + break; + default: + // User-defined messages + if (uMsg == WMU_SETTING_CHANGED) { + EnableWindow(GetDlgItem(hWnd, IDC_APPLY), TRUE); + } else { + // Not our message, ignore + return FALSE; + } + } + + return TRUE; +} \ No newline at end of file diff --git a/GrbyCfg/GrbyCfg.h b/GrbyCfg/GrbyCfg.h new file mode 100644 index 0000000..301a985 --- /dev/null +++ b/GrbyCfg/GrbyCfg.h @@ -0,0 +1,16 @@ +#pragma once + +#include "resource.h" + +// Define the config tabs +INT_PTR CALLBACK +GeneralTab_DlgProc(HWND hDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +INT_PTR CALLBACK +UploadTab_DlgProc(HWND hDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); \ No newline at end of file diff --git a/GrbyCfg/GrbyCfg.ico b/GrbyCfg/GrbyCfg.ico new file mode 100644 index 0000000..d551aa3 Binary files /dev/null and b/GrbyCfg/GrbyCfg.ico differ diff --git a/GrbyCfg/GrbyCfg.rc b/GrbyCfg/GrbyCfg.rc new file mode 100644 index 0000000..db7f070 --- /dev/null +++ b/GrbyCfg/GrbyCfg.rc @@ -0,0 +1,270 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Japanese resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN) +#ifdef _WIN32 +LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT +#pragma code_page(932) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_GRBYCFG ICON "GrbyCfg.ico" +IDI_SMALL ICON "small.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDC_GRBYCFG MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "E&xit", IDM_EXIT + END + POPUP "&Help" + BEGIN + MENUITEM "&About ...", IDM_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDC_GRBYCFG ACCELERATORS +BEGIN + "?", IDM_ABOUT, ASCII, ALT + "/", IDM_ABOUT, ASCII, ALT +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_APP_TITLE "GrbyCfg" + IDC_GRBYCFG "GRBYCFG" +END + +#endif // Japanese resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_GENCONFIG DIALOGEX 0, 0, 210, 206 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_BORDER +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + GROUPBOX "Screenshot Folder",IDC_STATIC,7,7,196,52,WS_GROUP + EDITTEXT IDC_SCRNDIR,13,19,184,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_SCRDIRBROWSE,147,39,50,14 + CONTROL "Keep screenshots after upload",IDC_KEEPIMGS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,39,114,14 + GROUPBOX "Launch Helper",IDC_STATIC,7,65,196,57 + LTEXT "Grabby is intended to be launched via a keyboard shortcut using a separate helper program. A basic helper is bundled.", + IDC_STATIC,13,77,184,25 + CONTROL "Launch Helper on Startup",IDC_LAUNCHHELPER,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,106,184,10 + GROUPBOX "Keyboard Shortcut",IDC_STATIC,7,128,196,71 + LTEXT "When the Launch Helper is running, take a screenshot with:", + IDC_STATIC,13,140,184,16 + CONTROL "Win+Shift+4 (Mac)",IDC_RDMAC,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,13,163,77,10 + CONTROL "Win+Shift+S (Win10)",IDC_RDWIN,"Button", + BS_AUTORADIOBUTTON,96,163,101,10 + CONTROL "Custom",IDC_RDCUSTOM,"Button",BS_AUTORADIOBUTTON,13,175, + 77,14 + EDITTEXT IDC_CUSTOMKEY,96,175,101,14,ES_AUTOHSCROLL | WS_GROUP +END + +IDD_UPLOADCONFIG DIALOGEX 0, 0, 210, 154 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_BORDER +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + GROUPBOX "Uploader",IDC_STATIC,7,7,196,88 + LTEXT "Enter the URL to use. Use ""%f"" to represent the filename, if it should be included in the URL.", + IDC_STATIC,11,19,186,17 + EDITTEXT IDC_URLINPUT,13,43,184,12,ES_AUTOHSCROLL + CONTROL "Encode filename in URL (for query parameters)", + IDC_ENCODEFILE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13, + 61,166,10 + LTEXT "HTTP method to use:",IDC_STATIC,13,77,68,12, + SS_CENTERIMAGE + COMBOBOX IDC_UPLOADMETHOD,87,77,110,30,CBS_DROPDOWNLIST | + CBS_SORT | CBS_UPPERCASE | WS_VSCROLL | WS_TABSTOP + GROUPBOX "Authentication",IDC_STATIC,7,101,196,40 +END + +IDD_CONFIGDIALOG DIALOGEX 0, 0, 234, 254 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Options" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,65,233,50,14 + PUSHBUTTON "Cancel",IDCANCEL,121,233,50,14 + CONTROL "",IDC_TABVIEW,"SysTabControl32",0x0,7,7,220,220 + PUSHBUTTON "Apply",IDC_APPLY,177,233,50,14,WS_DISABLED +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_GENCONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 203 + VERTGUIDE, 13 + VERTGUIDE, 90 + VERTGUIDE, 96 + VERTGUIDE, 197 + TOPMARGIN, 7 + BOTTOMMARGIN, 199 + HORZGUIDE, 19 + HORZGUIDE, 33 + HORZGUIDE, 39 + HORZGUIDE, 53 + HORZGUIDE, 59 + HORZGUIDE, 65 + HORZGUIDE, 77 + HORZGUIDE, 102 + HORZGUIDE, 106 + HORZGUIDE, 122 + HORZGUIDE, 128 + HORZGUIDE, 140 + HORZGUIDE, 156 + HORZGUIDE, 163 + HORZGUIDE, 173 + HORZGUIDE, 193 + HORZGUIDE, 199 + END + + IDD_UPLOADCONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 203 + VERTGUIDE, 13 + VERTGUIDE, 81 + VERTGUIDE, 87 + VERTGUIDE, 197 + TOPMARGIN, 7 + BOTTOMMARGIN, 147 + HORZGUIDE, 19 + HORZGUIDE, 36 + HORZGUIDE, 43 + HORZGUIDE, 61 + HORZGUIDE, 71 + HORZGUIDE, 77 + HORZGUIDE, 89 + HORZGUIDE, 95 + HORZGUIDE, 101 + END + + IDD_CONFIGDIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 227 + VERTGUIDE, 115 + VERTGUIDE, 121 + VERTGUIDE, 171 + VERTGUIDE, 177 + TOPMARGIN, 7 + BOTTOMMARGIN, 247 + HORZGUIDE, 227 + HORZGUIDE, 233 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/GrbyCfg/GrbyCfg.vcproj b/GrbyCfg/GrbyCfg.vcproj new file mode 100644 index 0000000..bb65d67 --- /dev/null +++ b/GrbyCfg/GrbyCfg.vcproj @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GrbyCfg/ReadMe.txt b/GrbyCfg/ReadMe.txt new file mode 100644 index 0000000..fc2c35e --- /dev/null +++ b/GrbyCfg/ReadMe.txt @@ -0,0 +1,53 @@ +======================================================================== + WIN32 APPLICATION : GrbyCfg Project Overview +======================================================================== + +AppWizard has created this GrbyCfg application for you. +This file contains a summary of what you will find in each of the files that +make up your GrbyCfg application. + + +GrbyCfg.vcproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +GrbyCfg.cpp + This is the main application source file. + +///////////////////////////////////////////////////////////////////////////// +AppWizard has created the following resources: + +GrbyCfg.rc + This is a listing of all of the Microsoft Windows resources that the + program uses. It includes the icons, bitmaps, and cursors that are stored + in the RES subdirectory. This file can be directly edited in Microsoft + Visual C++. + +Resource.h + This is the standard header file, which defines new resource IDs. + Microsoft Visual C++ reads and updates this file. +GrbyCfg.ico + This is an icon file, which is used as the application's icon (32x32). + This icon is included by the main resource file GrbyCfg.rc. + +small.ico + This is an icon file, which contains a smaller version (16x16) + of the application's icon. This icon is included by the main resource + file GrbyCfg.rc. + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named GrbyCfg.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/GrbyCfg/UploadCfg.c b/GrbyCfg/UploadCfg.c new file mode 100644 index 0000000..b0a2fb6 --- /dev/null +++ b/GrbyCfg/UploadCfg.c @@ -0,0 +1,11 @@ +#include "stdafx.h" + +INT_PTR CALLBACK +UploadTab_DlgProc(HWND hDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + // TODO: anything + return FALSE; +} \ No newline at end of file diff --git a/GrbyCfg/Uploader.h b/GrbyCfg/Uploader.h new file mode 100644 index 0000000..e69de29 diff --git a/GrbyCfg/resource.h b/GrbyCfg/resource.h new file mode 100644 index 0000000..ce6dc24 --- /dev/null +++ b/GrbyCfg/resource.h @@ -0,0 +1,42 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by GrbyCfg.rc +// +#define IDC_MYICON 2 +#define IDD_CONFIGDIALOG 9 +#define IDD_GRBYCFG_DIALOG 102 +#define IDS_APP_TITLE 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDD_GENCONFIG 106 +#define IDI_GRBYCFG 107 +#define IDI_SMALL 108 +#define IDC_GRBYCFG 109 +#define IDR_MAINFRAME 128 +#define IDD_UPLOADCONFIG 130 +#define IDC_TABVIEW 1000 +#define IDC_APPLY 1001 +#define IDC_SCRNDIR 1004 +#define IDC_SCRDIRBROWSE 1005 +#define IDC_KEEPIMGS 1006 +#define IDC_URLINPUT 1007 +#define IDC_LAUNCHHELPER 1007 +#define IDC_ENCODEFILE 1008 +#define IDC_RDMAC 1008 +#define IDC_UPLOADMETHOD 1009 +#define IDC_RDWIN 1009 +#define IDC_RDCUSTOM 1010 +#define IDC_CUSTOMKEY 1011 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 130 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1012 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/GrbyCfg/small.ico b/GrbyCfg/small.ico new file mode 100644 index 0000000..d551aa3 Binary files /dev/null and b/GrbyCfg/small.ico differ diff --git a/GrbyCfg/stdafx.c b/GrbyCfg/stdafx.c new file mode 100644 index 0000000..f840850 --- /dev/null +++ b/GrbyCfg/stdafx.c @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// GrbyCfg.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/GrbyCfg/stdafx.h b/GrbyCfg/stdafx.h new file mode 100644 index 0000000..7423c8d --- /dev/null +++ b/GrbyCfg/stdafx.h @@ -0,0 +1,26 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include +#include +#include +#define _WIN32_DCOM +#define COBJMACROS +#define CINTERFACE +#include +#include +// C RunTime Header Files +#include +#include +#include + +#include +#include "Dialog.h" +#include "resource.h" \ No newline at end of file diff --git a/Resource.h b/Resource.h deleted file mode 100644 index 6bcd5f7..0000000 --- a/Resource.h +++ /dev/null @@ -1,29 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by Grabby.rc -// - -#define IDS_APP_TITLE 103 - -#define IDR_MAINFRAME 128 -#define IDD_GRABBY_DIALOG 102 -#define IDD_ABOUTBOX 103 -#define IDM_ABOUT 104 -#define IDM_EXIT 105 -#define IDI_GRABBY 107 -#define IDI_SMALL 108 -#define IDC_GRABBY 109 -#define IDC_MYICON 2 -#define IDC_STATIC -1 -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS - -#define _APS_NO_MFC 130 -#define _APS_NEXT_RESOURCE_VALUE 129 -#define _APS_NEXT_COMMAND_VALUE 32771 -#define _APS_NEXT_CONTROL_VALUE 1000 -#define _APS_NEXT_SYMED_VALUE 110 -#endif -#endif