5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-12 06:59:30 +08:00
wails/v2/internal/ffenestri/ffenestri_windows.cpp

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) {
}