mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-12 06:59:30 +08:00
417 lines
12 KiB
C++
417 lines
12 KiB
C++
// Some code may be inspired by or directly used from Webview.
|
|
#include "ffenestri_windows.h"
|
|
|
|
int debug = 0;
|
|
DWORD mainThread;
|
|
|
|
typedef void(*dispatchMethod)(void);
|
|
typedef void(*dispatchMethod1)(void*);
|
|
typedef void(*dispatchMethod2)(void*,void*);
|
|
|
|
typedef struct {
|
|
void* func;
|
|
void* args[5];
|
|
int argc;
|
|
} dispatchFunction;
|
|
|
|
dispatchFunction* NewDispatchFunction(void *func) {
|
|
dispatchFunction *result = (dispatchFunction *)malloc(sizeof(dispatchFunction));
|
|
result->func = func;
|
|
result->argc = 0;
|
|
return result;
|
|
}
|
|
|
|
// dispatch will execute the given `func` pointer
|
|
void dispatch(dispatchFunction *func) {
|
|
PostThreadMessage(mainThread, WM_APP, 0, (LPARAM) func);
|
|
}
|
|
|
|
struct Application{
|
|
// Window specific
|
|
HWND window;
|
|
|
|
// Application
|
|
const char *title;
|
|
int width;
|
|
int height;
|
|
int resizable;
|
|
int devtools;
|
|
int fullscreen;
|
|
int startHidden;
|
|
int logLevel;
|
|
int hideWindowOnClose;
|
|
int minSizeSet;
|
|
LONG minWidth;
|
|
LONG minHeight;
|
|
int maxSizeSet;
|
|
LONG maxWidth;
|
|
LONG maxHeight;
|
|
int frame;
|
|
};
|
|
|
|
struct Application *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose) {
|
|
|
|
// Create application
|
|
struct Application *result = (struct Application*)malloc(sizeof(struct Application));
|
|
|
|
result->title = title;
|
|
result->width = width;
|
|
result->height = height;
|
|
result->resizable = resizable;
|
|
result->devtools = devtools;
|
|
result->fullscreen = fullscreen;
|
|
result->startHidden = startHidden;
|
|
result->logLevel = logLevel;
|
|
result->hideWindowOnClose = hideWindowOnClose;
|
|
|
|
// Min/Max Width/Height
|
|
result->minWidth = 0;
|
|
result->minHeight = 0;
|
|
result->maxWidth = 0;
|
|
result->maxHeight = 0;
|
|
|
|
// Have a frame by default
|
|
result->frame = 1;
|
|
|
|
// Capture Main Thread
|
|
mainThread = GetCurrentThreadId();
|
|
|
|
return result;
|
|
}
|
|
|
|
void SetMinWindowSize(struct Application* app, int minWidth, int minHeight) {
|
|
app->minWidth = (LONG)minWidth;
|
|
app->minHeight = (LONG)minHeight;
|
|
}
|
|
|
|
void SetMaxWindowSize(struct Application* app, int maxWidth, int maxHeight) {
|
|
app->maxWidth = (LONG)maxWidth;
|
|
app->maxHeight = (LONG)maxHeight;
|
|
}
|
|
|
|
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
|
|
|
struct Application *app = (struct Application *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
switch(msg) {
|
|
|
|
case WM_DESTROY: {
|
|
DestroyApplication(app);
|
|
return 0;
|
|
}
|
|
case WM_GETMINMAXINFO: {
|
|
// Exit early if this is called before the window is created.
|
|
if ( app == NULL ) {
|
|
return 0;
|
|
}
|
|
|
|
// get pixel density
|
|
HDC hDC = GetDC(NULL);
|
|
double DPIScaleX = GetDeviceCaps(hDC, 88)/96.0;
|
|
double DPIScaleY = GetDeviceCaps(hDC, 90)/96.0;
|
|
ReleaseDC(NULL, hDC);
|
|
|
|
RECT rcClient, rcWind;
|
|
POINT ptDiff;
|
|
GetClientRect(hwnd, &rcClient);
|
|
GetWindowRect(hwnd, &rcWind);
|
|
|
|
int widthExtra = (rcWind.right - rcWind.left) - rcClient.right;
|
|
int heightExtra = (rcWind.bottom - rcWind.top) - rcClient.bottom;
|
|
|
|
LPMINMAXINFO mmi = (LPMINMAXINFO) lParam;
|
|
if (app->minWidth > 0 && app->minHeight > 0) {
|
|
mmi->ptMinTrackSize.x = app->minWidth * DPIScaleX + widthExtra;
|
|
mmi->ptMinTrackSize.y = app->minHeight * DPIScaleY + heightExtra;
|
|
}
|
|
if (app->maxWidth > 0 && app->maxHeight > 0) {
|
|
mmi->ptMaxSize.x = app->maxWidth * DPIScaleX + widthExtra;
|
|
mmi->ptMaxSize.y = app->maxHeight * DPIScaleY + heightExtra;
|
|
mmi->ptMaxTrackSize.x = app->maxWidth * DPIScaleX + widthExtra;
|
|
mmi->ptMaxTrackSize.y = app->maxHeight * DPIScaleY + heightExtra;
|
|
}
|
|
return 0;
|
|
}
|
|
default:
|
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
}
|
|
|
|
void Run(struct Application* app, int argc, char **argv) {
|
|
|
|
WNDCLASSEX wc;
|
|
HINSTANCE hInstance = GetModuleHandle(NULL);
|
|
ZeroMemory(&wc, sizeof(WNDCLASSEX));
|
|
wc.cbSize = sizeof(WNDCLASSEX);
|
|
wc.hInstance = hInstance;
|
|
wc.lpszClassName = (LPCWSTR)"ffenestri";
|
|
wc.lpfnWndProc = WndProc;
|
|
|
|
|
|
// Process window resizable
|
|
DWORD windowStyle = WS_OVERLAPPEDWINDOW;
|
|
if (app->resizable == 0) {
|
|
windowStyle &= ~WS_MAXIMIZEBOX;
|
|
windowStyle &= ~WS_THICKFRAME;
|
|
}
|
|
if ( app->frame == 0 ) {
|
|
windowStyle = WS_POPUP;
|
|
}
|
|
|
|
RegisterClassEx(&wc);
|
|
app->window = CreateWindow((LPCWSTR)"ffenestri", (LPCWSTR)"", windowStyle, CW_USEDEFAULT,
|
|
CW_USEDEFAULT, app->width, app->height, NULL, NULL,
|
|
GetModuleHandle(NULL), NULL);
|
|
// Set Title
|
|
setTitle(app, app->title);
|
|
|
|
|
|
// Store application pointer in window handle
|
|
SetWindowLongPtr(app->window, GWLP_USERDATA, (LONG_PTR)app);
|
|
|
|
// Process whether window should show by default
|
|
int startVisibility = SW_SHOWNORMAL;
|
|
if ( app->startHidden == 1 ) {
|
|
startVisibility = SW_HIDE;
|
|
}
|
|
// private center() as we are on main thread
|
|
center(app);
|
|
ShowWindow(app->window, startVisibility);
|
|
UpdateWindow(app->window);
|
|
SetFocus(app->window);
|
|
|
|
// TODO: Add webview2
|
|
|
|
// Main event loop
|
|
MSG msg;
|
|
BOOL res;
|
|
while ((res = GetMessage(&msg, NULL, 0, 0)) != -1) {
|
|
if (msg.hwnd) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
continue;
|
|
}
|
|
if (msg.message == WM_APP) {
|
|
dispatchFunction *m = (dispatchFunction*) msg.lParam;
|
|
void *method = m->func;
|
|
if (m->argc == 1) {
|
|
((dispatchMethod1)method)(m->args[0]);
|
|
}
|
|
if (m->argc == 2) {
|
|
((dispatchMethod2)method)(m->args[0], m->args[1]);
|
|
}
|
|
free(m);
|
|
} else if (msg.message == WM_QUIT) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DestroyApplication(struct Application* app) {
|
|
PostQuitMessage(0);
|
|
}
|
|
void SetDebug(struct Application* app, int flag) {
|
|
debug = flag;
|
|
}
|
|
|
|
void SetBindings(struct Application* app, const char *bindings) {
|
|
}
|
|
|
|
void ExecJS(struct Application* app, const char *script) {
|
|
}
|
|
|
|
void hide(struct Application* app) {
|
|
ShowWindow(app->window, SW_HIDE);
|
|
}
|
|
|
|
void Hide(struct Application* app) {
|
|
dispatchFunction *f = NewDispatchFunction((void*)hide);
|
|
f->args[0] = app;
|
|
f->argc = 1;
|
|
dispatch(f);
|
|
}
|
|
|
|
void show(struct Application* app) {
|
|
ShowWindow(app->window, SW_SHOW);
|
|
}
|
|
|
|
void Show(struct Application* app) {
|
|
dispatchFunction *f = NewDispatchFunction((void*)show);
|
|
f->args[0] = app;
|
|
f->argc = 1;
|
|
dispatch(f);
|
|
}
|
|
|
|
void center(struct Application* app) {
|
|
|
|
HMONITOR currentMonitor = MonitorFromWindow(app->window, MONITOR_DEFAULTTONEAREST);
|
|
MONITORINFO info = {0};
|
|
info.cbSize = sizeof(info);
|
|
GetMonitorInfoA(currentMonitor, &info);
|
|
RECT workRect = info.rcWork;
|
|
LONG screenMiddleW = (workRect.right - workRect.left) / 2;
|
|
LONG screenMiddleH = (workRect.bottom - workRect.top) / 2;
|
|
RECT winRect;
|
|
if (app->frame == 1) {
|
|
GetWindowRect(app->window, &winRect);
|
|
} else {
|
|
GetClientRect(app->window, &winRect);
|
|
}
|
|
LONG winWidth = winRect.right - winRect.left;
|
|
LONG winHeight = winRect.bottom - winRect.top;
|
|
|
|
LONG windowX = screenMiddleW - (winWidth / 2);
|
|
LONG windowY = screenMiddleH - (winHeight / 2);
|
|
|
|
SetWindowPos(app->window, HWND_TOP, windowX, windowY, winWidth, winHeight, SWP_NOSIZE);
|
|
}
|
|
|
|
void Center(struct Application* app) {
|
|
dispatchFunction *f = NewDispatchFunction((void*)center);
|
|
f->args[0] = app;
|
|
f->argc = 1;
|
|
dispatch(f);
|
|
}
|
|
|
|
UINT getWindowPlacement(struct Application* app) {
|
|
WINDOWPLACEMENT lpwndpl;
|
|
lpwndpl.length = sizeof(WINDOWPLACEMENT);
|
|
BOOL result = GetWindowPlacement(app->window, &lpwndpl);
|
|
if( result == 0 ) {
|
|
// TODO: Work out what this call failing means
|
|
return -1;
|
|
}
|
|
return lpwndpl.showCmd;
|
|
}
|
|
|
|
int isMaximised(struct Application* app) {
|
|
return getWindowPlacement(app) == SW_SHOWMAXIMIZED;
|
|
}
|
|
|
|
void maximise(struct Application* app) {
|
|
ShowWindow(app->window, SW_MAXIMIZE);
|
|
}
|
|
|
|
void Maximise(struct Application* app) {
|
|
dispatchFunction *f = NewDispatchFunction((void*)maximise);
|
|
f->args[0] = app;
|
|
f->argc = 1;
|
|
dispatch(f);
|
|
}
|
|
|
|
void unmaximise(struct Application* app) {
|
|
ShowWindow(app->window, SW_RESTORE);
|
|
}
|
|
|
|
void Unmaximise(struct Application* app) {
|
|
dispatchFunction *f = NewDispatchFunction((void*)unmaximise);
|
|
f->args[0] = app;
|
|
f->argc = 1;
|
|
dispatch(f);
|
|
}
|
|
|
|
|
|
void ToggleMaximise(struct Application* app) {
|
|
if(isMaximised(app)) {
|
|
return Unmaximise(app);
|
|
}
|
|
return Maximise(app);
|
|
}
|
|
|
|
int isMinimised(struct Application* app) {
|
|
return getWindowPlacement(app) == SW_SHOWMINIMIZED;
|
|
}
|
|
|
|
void minimise(struct Application* app) {
|
|
ShowWindow(app->window, SW_MINIMIZE);
|
|
}
|
|
|
|
void Minimise(struct Application* app) {
|
|
dispatchFunction *f = NewDispatchFunction((void*)minimise);
|
|
f->args[0] = app;
|
|
f->argc = 1;
|
|
dispatch(f);
|
|
}
|
|
|
|
void unminimise(struct Application* app) {
|
|
ShowWindow(app->window, SW_RESTORE);
|
|
}
|
|
|
|
void Unminimise(struct Application* app) {
|
|
dispatchFunction *f = NewDispatchFunction((void*)unminimise);
|
|
f->args[0] = app;
|
|
f->argc = 1;
|
|
dispatch(f);
|
|
}
|
|
|
|
void ToggleMinimise(struct Application* app) {
|
|
if(isMinimised(app)) {
|
|
return Unminimise(app);
|
|
}
|
|
return Minimise(app);
|
|
}
|
|
|
|
void SetColour(struct Application* app, int red, int green, int blue, int alpha) {
|
|
|
|
}
|
|
|
|
void SetSize(struct Application* app, int width, int height) {
|
|
}
|
|
void SetPosition(struct Application* app, int x, int y) {
|
|
}
|
|
void Quit(struct Application* app) {
|
|
}
|
|
|
|
// Credit: https://stackoverflow.com/a/6693107
|
|
void setTitle(struct Application* app, const char *title) {
|
|
int wchars_num = MultiByteToWideChar( CP_UTF8 , 0 , title , -1, NULL , 0 );
|
|
wchar_t* wstr = new wchar_t[wchars_num];
|
|
MultiByteToWideChar( CP_UTF8 , 0 , title , -1, wstr , wchars_num );
|
|
SetWindowText(app->window, wstr);
|
|
delete[] wstr;
|
|
}
|
|
|
|
void SetTitle(struct Application* app, const char *title) {
|
|
dispatchFunction *f = NewDispatchFunction((void*)setTitle);
|
|
f->args[0] = app;
|
|
f->args[1] = (void*)title;
|
|
f->argc = 2;
|
|
dispatch(f);
|
|
}
|
|
|
|
void Fullscreen(struct Application* app) {
|
|
}
|
|
|
|
void UnFullscreen(struct Application* app) {
|
|
}
|
|
|
|
void ToggleFullscreen(struct Application* app) {
|
|
}
|
|
|
|
void DisableFrame(struct Application* app) {
|
|
app->frame = 0;
|
|
}
|
|
|
|
void OpenDialog(struct Application* app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolvesAliases, int treatPackagesAsDirectories) {
|
|
}
|
|
void SaveDialog(struct Application* app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories) {
|
|
}
|
|
void MessageDialog(struct Application* app, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton) {
|
|
}
|
|
void DarkModeEnabled(struct Application* app, char *callbackID) {
|
|
}
|
|
void SetApplicationMenu(struct Application* app, const char *applicationMenuJSON) {
|
|
}
|
|
void AddTrayMenu(struct Application* app, const char *menuTrayJSON) {
|
|
}
|
|
void SetTrayMenu(struct Application* app, const char *menuTrayJSON) {
|
|
}
|
|
void DeleteTrayMenuByID(struct Application* app, const char *id) {
|
|
}
|
|
void UpdateTrayMenuLabel(struct Application* app, const char* JSON) {
|
|
}
|
|
void AddContextMenu(struct Application* app, char *contextMenuJSON) {
|
|
}
|
|
void UpdateContextMenu(struct Application* app, char *contextMenuJSON) {
|
|
} |