From a5bf76b30f60107b6e5afdeec7c278005076ef53 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Tue, 29 Mar 2022 19:25:50 +1100 Subject: [PATCH] Fix up dev. Pull in go-webview and winc --- v2/cmd/wails/internal/commands/dev/dev.go | 13 +- .../commands/generate/template/base/go.sum | 4 - .../generate/template/base/go.tmpl.mod | 64 +- .../templates/templates/svelte/go.mod.tmpl | 4 +- .../templates/templates/svelte/go.sum | 4 - .../templates/templates/vanilla/go.mod.tmpl | 4 +- .../templates/templates/vanilla/go.sum | 4 - v2/go.mod | 4 +- v2/go.sum | 8 - v2/internal/appng/app_bindings.go | 2 +- v2/internal/appng/app_default_windows.go | 2 +- v2/internal/appng/app_dev.go | 2 +- v2/internal/binding/generate.go | 11 +- .../windows/wv2runtime/wv2runtime.go | 2 +- .../frontend/desktop/windows/frontend.go | 6 +- .../desktop/windows/go-webview2/LICENSE | 22 + .../desktop/windows/go-webview2/README.md | 27 + .../windows/go-webview2/internal/w32/w32.go | 154 + .../pkg/edge/COREWEBVIEW2_COLOR.go | 8 + .../COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND.go | 9 + .../pkg/edge/COREWEBVIEW2_KEY_EVENT_KIND.go | 10 + .../edge/COREWEBVIEW2_MOVE_FOCUS_REASON.go | 9 + .../edge/COREWEBVIEW2_PHYSICAL_KEY_STATUS.go | 10 + .../edge/COREWEBVIEW2_WEB_RESOURCE_CONTEXT.go | 23 + ...eWebView2AcceleratorKeyPressedEventArgs.go | 76 + ...bView2AcceleratorKeyPressedEventHandler.go | 51 + .../pkg/edge/ICoreWebView2Controller.go | 132 + .../pkg/edge/ICoreWebView2Controller2.go | 72 + ...eCoreWebView2ControllerCompletedHandler.go | 51 + ...oreWebView2NavigationCompletedEventArgs.go | 16 + ...WebView2NavigationCompletedEventHandler.go | 51 + .../pkg/edge/ICoreWebView2Settings.go | 268 ++ .../edge/ICoreWebView2WebResourceRequest.go | 46 + ...reWebView2WebResourceRequestedEventArgs.go | 49 + ...ebView2WebResourceRequestedEventHandler.go | 48 + .../edge/ICoreWebView2WebResourceResponse.go | 26 + .../go-webview2/pkg/edge/ICoreWebView2_2.go | 16 + .../go-webview2/pkg/edge/ICoreWebView2_3.go | 60 + .../pkg/edge/ICoreWebViewSettings.go | 394 ++ .../windows/go-webview2/pkg/edge/IStream.go | 15 + .../windows/go-webview2/pkg/edge/chromium.go | 351 ++ .../go-webview2/pkg/edge/chromium_386.go | 24 + .../go-webview2/pkg/edge/chromium_amd64.go | 21 + .../go-webview2/pkg/edge/chromium_arm64.go | 26 + .../go-webview2/pkg/edge/corewebview2.go | 480 +++ .../windows/go-webview2/pkg/edge/guid.go | 223 ++ .../webviewloader/arm64/WebView2Loader.dll | Bin 0 -> 124328 bytes .../go-webview2/webviewloader/module.go | 140 + .../go-webview2/webviewloader/module_386.go | 6 + .../go-webview2/webviewloader/module_amd64.go | 6 + .../go-webview2/webviewloader/module_arm64.go | 6 + .../webviewloader/x64/WebView2Loader.dll | Bin 0 -> 137640 bytes .../webviewloader/x86/WebView2Loader.dll | Bin 0 -> 107416 bytes v2/internal/frontend/desktop/windows/keys.go | 2 +- v2/internal/frontend/desktop/windows/menu.go | 2 +- .../frontend/desktop/windows/winc/.gitignore | 12 + .../frontend/desktop/windows/winc/AUTHORS | 12 + .../frontend/desktop/windows/winc/LICENSE | 21 + .../frontend/desktop/windows/winc/README.md | 181 + .../frontend/desktop/windows/winc/app.go | 108 + .../frontend/desktop/windows/winc/bitmap.go | 110 + .../frontend/desktop/windows/winc/brush.go | 72 + .../frontend/desktop/windows/winc/buttons.go | 154 + .../frontend/desktop/windows/winc/canvas.go | 157 + .../frontend/desktop/windows/winc/color.go | 24 + .../frontend/desktop/windows/winc/combobox.go | 68 + .../desktop/windows/winc/commondlgs.go | 123 + .../desktop/windows/winc/controlbase.go | 518 +++ .../desktop/windows/winc/controller.go | 83 + .../frontend/desktop/windows/winc/dialog.go | 134 + .../desktop/windows/winc/dock_topbottom.png | Bin 0 -> 4373 bytes .../desktop/windows/winc/dock_topleft.png | Bin 0 -> 3556 bytes .../frontend/desktop/windows/winc/edit.go | 111 + .../frontend/desktop/windows/winc/event.go | 15 + .../desktop/windows/winc/eventdata.go | 50 + .../desktop/windows/winc/eventmanager.go | 22 + .../frontend/desktop/windows/winc/font.go | 119 + .../frontend/desktop/windows/winc/form.go | 300 ++ .../desktop/windows/winc/globalvars.go | 25 + .../frontend/desktop/windows/winc/icon.go | 53 + .../desktop/windows/winc/imagelist.go | 62 + .../desktop/windows/winc/imageview.go | 57 + .../desktop/windows/winc/imageviewbox.go | 340 ++ .../frontend/desktop/windows/winc/init.go | 19 + .../frontend/desktop/windows/winc/keyboard.go | 438 ++ .../frontend/desktop/windows/winc/label.go | 29 + .../frontend/desktop/windows/winc/layout.go | 220 + .../frontend/desktop/windows/winc/listview.go | 547 +++ .../frontend/desktop/windows/winc/menu.go | 337 ++ .../desktop/windows/winc/mousecontrol.go | 48 + .../windows/winc/msghandlerregistry.go | 26 + .../frontend/desktop/windows/winc/panel.go | 198 + .../frontend/desktop/windows/winc/path.go | 75 + .../frontend/desktop/windows/winc/pen.go | 59 + .../desktop/windows/winc/progressbar.go | 46 + .../frontend/desktop/windows/winc/rect.go | 85 + .../frontend/desktop/windows/winc/resizer.go | 214 + .../desktop/windows/winc/scrollview.go | 119 + .../frontend/desktop/windows/winc/slider.go | 77 + .../frontend/desktop/windows/winc/tabview.go | 105 + .../frontend/desktop/windows/winc/toolbar.go | 179 + .../frontend/desktop/windows/winc/tooltip.go | 43 + .../frontend/desktop/windows/winc/treeview.go | 284 ++ .../frontend/desktop/windows/winc/utils.go | 149 + .../desktop/windows/winc/w32/comctl32.go | 110 + .../desktop/windows/winc/w32/comdlg32.go | 39 + .../desktop/windows/winc/w32/constants.go | 3525 +++++++++++++++++ .../desktop/windows/winc/w32/dwmapi.go | 18 + .../desktop/windows/winc/w32/gdi32.go | 525 +++ .../desktop/windows/winc/w32/gdiplus.go | 176 + .../desktop/windows/winc/w32/idispatch.go | 44 + .../desktop/windows/winc/w32/istream.go | 32 + .../desktop/windows/winc/w32/iunknown.go | 28 + .../desktop/windows/winc/w32/kernel32.go | 331 ++ .../desktop/windows/winc/w32/ole32.go | 64 + .../desktop/windows/winc/w32/oleaut32.go | 49 + .../desktop/windows/winc/w32/shcore.go | 27 + .../desktop/windows/winc/w32/shell32.go | 234 ++ .../desktop/windows/winc/w32/shlwapi.go | 24 + .../desktop/windows/winc/w32/toolbar.go | 214 + .../desktop/windows/winc/w32/typedef.go | 1079 +++++ .../desktop/windows/winc/w32/user32.go | 1260 ++++++ .../desktop/windows/winc/w32/utils.go | 232 ++ .../desktop/windows/winc/w32/uxtheme.go | 150 + .../frontend/desktop/windows/winc/w32/vars.go | 14 + .../frontend/desktop/windows/winc/wndproc.go | 152 + .../frontend/desktop/windows/window.go | 4 +- v2/internal/gomod/gomod_test.go | 48 +- v2/internal/system/system_windows.go | 2 +- v2/pkg/commands/build/base.go | 11 - website/docs/reference/cli.mdx | 5 +- 131 files changed, 17561 insertions(+), 128 deletions(-) create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/LICENSE create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/README.md create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/internal/w32/w32.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_COLOR.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_KEY_EVENT_KIND.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_MOVE_FOCUS_REASON.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_PHYSICAL_KEY_STATUS.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_WEB_RESOURCE_CONTEXT.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2AcceleratorKeyPressedEventArgs.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2AcceleratorKeyPressedEventHandler.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2Controller.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2Controller2.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2CreateCoreWebView2ControllerCompletedHandler.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2NavigationCompletedEventArgs.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2NavigationCompletedEventHandler.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2Settings.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2WebResourceRequest.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2WebResourceRequestedEventArgs.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2WebResourceRequestedEventHandler.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2WebResourceResponse.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2_2.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2_3.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebViewSettings.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/IStream.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/chromium.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/chromium_386.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/chromium_amd64.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/chromium_arm64.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/corewebview2.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/guid.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/webviewloader/arm64/WebView2Loader.dll create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/webviewloader/module.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/webviewloader/module_386.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/webviewloader/module_amd64.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/webviewloader/module_arm64.go create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/webviewloader/x64/WebView2Loader.dll create mode 100644 v2/internal/frontend/desktop/windows/go-webview2/webviewloader/x86/WebView2Loader.dll create mode 100644 v2/internal/frontend/desktop/windows/winc/.gitignore create mode 100644 v2/internal/frontend/desktop/windows/winc/AUTHORS create mode 100644 v2/internal/frontend/desktop/windows/winc/LICENSE create mode 100644 v2/internal/frontend/desktop/windows/winc/README.md create mode 100644 v2/internal/frontend/desktop/windows/winc/app.go create mode 100644 v2/internal/frontend/desktop/windows/winc/bitmap.go create mode 100644 v2/internal/frontend/desktop/windows/winc/brush.go create mode 100644 v2/internal/frontend/desktop/windows/winc/buttons.go create mode 100644 v2/internal/frontend/desktop/windows/winc/canvas.go create mode 100644 v2/internal/frontend/desktop/windows/winc/color.go create mode 100644 v2/internal/frontend/desktop/windows/winc/combobox.go create mode 100644 v2/internal/frontend/desktop/windows/winc/commondlgs.go create mode 100644 v2/internal/frontend/desktop/windows/winc/controlbase.go create mode 100644 v2/internal/frontend/desktop/windows/winc/controller.go create mode 100644 v2/internal/frontend/desktop/windows/winc/dialog.go create mode 100644 v2/internal/frontend/desktop/windows/winc/dock_topbottom.png create mode 100644 v2/internal/frontend/desktop/windows/winc/dock_topleft.png create mode 100644 v2/internal/frontend/desktop/windows/winc/edit.go create mode 100644 v2/internal/frontend/desktop/windows/winc/event.go create mode 100644 v2/internal/frontend/desktop/windows/winc/eventdata.go create mode 100644 v2/internal/frontend/desktop/windows/winc/eventmanager.go create mode 100644 v2/internal/frontend/desktop/windows/winc/font.go create mode 100644 v2/internal/frontend/desktop/windows/winc/form.go create mode 100644 v2/internal/frontend/desktop/windows/winc/globalvars.go create mode 100644 v2/internal/frontend/desktop/windows/winc/icon.go create mode 100644 v2/internal/frontend/desktop/windows/winc/imagelist.go create mode 100644 v2/internal/frontend/desktop/windows/winc/imageview.go create mode 100644 v2/internal/frontend/desktop/windows/winc/imageviewbox.go create mode 100644 v2/internal/frontend/desktop/windows/winc/init.go create mode 100644 v2/internal/frontend/desktop/windows/winc/keyboard.go create mode 100644 v2/internal/frontend/desktop/windows/winc/label.go create mode 100644 v2/internal/frontend/desktop/windows/winc/layout.go create mode 100644 v2/internal/frontend/desktop/windows/winc/listview.go create mode 100644 v2/internal/frontend/desktop/windows/winc/menu.go create mode 100644 v2/internal/frontend/desktop/windows/winc/mousecontrol.go create mode 100644 v2/internal/frontend/desktop/windows/winc/msghandlerregistry.go create mode 100644 v2/internal/frontend/desktop/windows/winc/panel.go create mode 100644 v2/internal/frontend/desktop/windows/winc/path.go create mode 100644 v2/internal/frontend/desktop/windows/winc/pen.go create mode 100644 v2/internal/frontend/desktop/windows/winc/progressbar.go create mode 100644 v2/internal/frontend/desktop/windows/winc/rect.go create mode 100644 v2/internal/frontend/desktop/windows/winc/resizer.go create mode 100644 v2/internal/frontend/desktop/windows/winc/scrollview.go create mode 100644 v2/internal/frontend/desktop/windows/winc/slider.go create mode 100644 v2/internal/frontend/desktop/windows/winc/tabview.go create mode 100644 v2/internal/frontend/desktop/windows/winc/toolbar.go create mode 100644 v2/internal/frontend/desktop/windows/winc/tooltip.go create mode 100644 v2/internal/frontend/desktop/windows/winc/treeview.go create mode 100644 v2/internal/frontend/desktop/windows/winc/utils.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/comctl32.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/comdlg32.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/constants.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/dwmapi.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/gdi32.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/gdiplus.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/idispatch.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/istream.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/iunknown.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/kernel32.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/ole32.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/oleaut32.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/shcore.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/shell32.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/shlwapi.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/toolbar.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/typedef.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/user32.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/utils.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/uxtheme.go create mode 100644 v2/internal/frontend/desktop/windows/winc/w32/vars.go create mode 100644 v2/internal/frontend/desktop/windows/winc/wndproc.go diff --git a/v2/cmd/wails/internal/commands/dev/dev.go b/v2/cmd/wails/internal/commands/dev/dev.go index 89ce03a5b..7cc39114e 100644 --- a/v2/cmd/wails/internal/commands/dev/dev.go +++ b/v2/cmd/wails/internal/commands/dev/dev.go @@ -73,6 +73,7 @@ type devFlags struct { debounceMS int devServerURL string appargs string + saveConfig bool } // AddSubcommand adds the `dev` command for the Wails application @@ -96,6 +97,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error { command.IntFlag("debounce", "The amount of time to wait to trigger a reload on change", &flags.debounceMS) command.StringFlag("devserverurl", "The url of the dev server to use", &flags.devServerURL) command.StringFlag("appargs", "arguments to pass to the underlying app (quoted and space searated)", &flags.appargs) + command.BoolFlag("save", "Save given flags as defaults", &flags.saveConfig) command.Action(func() error { // Create logger @@ -294,15 +296,12 @@ func loadAndMergeProjectConfig(cwd string, flags *devFlags) (*project.Project, e return nil, err } - var shouldSaveConfig bool - if flags.assetDir == "" && projectConfig.AssetDirectory != "" { flags.assetDir = projectConfig.AssetDirectory } if flags.assetDir != projectConfig.AssetDirectory { projectConfig.AssetDirectory = filepath.ToSlash(flags.assetDir) - shouldSaveConfig = true } if flags.assetDir != "" { @@ -318,7 +317,6 @@ func loadAndMergeProjectConfig(cwd string, flags *devFlags) (*project.Project, e if flags.reloadDirs != projectConfig.ReloadDirectories { projectConfig.ReloadDirectories = filepath.ToSlash(flags.reloadDirs) - shouldSaveConfig = true } if flags.devServerURL == defaultDevServerURL && projectConfig.DevServerURL != defaultDevServerURL && projectConfig.DevServerURL != "" { @@ -327,7 +325,6 @@ func loadAndMergeProjectConfig(cwd string, flags *devFlags) (*project.Project, e if flags.devServerURL != projectConfig.DevServerURL { projectConfig.DevServerURL = flags.devServerURL - shouldSaveConfig = true } if flags.wailsjsdir == "" && projectConfig.WailsJSDir != "" { @@ -340,7 +337,6 @@ func loadAndMergeProjectConfig(cwd string, flags *devFlags) (*project.Project, e if flags.wailsjsdir != projectConfig.WailsJSDir { projectConfig.WailsJSDir = filepath.ToSlash(flags.wailsjsdir) - shouldSaveConfig = true } if flags.debounceMS == 100 && projectConfig.DebounceMS != 100 { @@ -352,14 +348,13 @@ func loadAndMergeProjectConfig(cwd string, flags *devFlags) (*project.Project, e if flags.debounceMS != projectConfig.DebounceMS { projectConfig.DebounceMS = flags.debounceMS - shouldSaveConfig = true } if flags.appargs == "" && projectConfig.AppArgs != "" { flags.appargs = projectConfig.AppArgs } - if shouldSaveConfig { + if flags.saveConfig { err = projectConfig.Save() if err != nil { return nil, err @@ -584,7 +579,7 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc LogRed("Error during build: %s", err.Error()) continue } - // If we have a new process, save it + // If we have a new process, saveConfig it if newBinaryProcess != nil { debugBinaryProcess = newBinaryProcess } diff --git a/v2/cmd/wails/internal/commands/generate/template/base/go.sum b/v2/cmd/wails/internal/commands/generate/template/base/go.sum index 86e6a1fd8..3c219b526 100644 --- a/v2/cmd/wails/internal/commands/generate/template/base/go.sum +++ b/v2/cmd/wails/internal/commands/generate/template/base/go.sum @@ -82,16 +82,12 @@ github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQ github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM= github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y= github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0= -github.com/leaanthony/go-webview2 v0.0.0-20210914103035-f00aa774a934 h1:nK/JTPyJi5QRqYjVZjXgtN4/dhg2qtngoLxLDVn429k= -github.com/leaanthony/go-webview2 v0.0.0-20210914103035-f00aa774a934/go.mod h1:lS5ds4bruPk9d7lzdF/OH31Z0YCerI6MmHNFGsWoUnM= github.com/leaanthony/gosod v1.0.2/go.mod h1:W8RyeSFBXu7RpIxPGEJfW4moSyGGEjlJMLV25wEbAdU= github.com/leaanthony/idgen v1.0.0/go.mod h1:4nBZnt8ml/f/ic/EVQuLxuj817RccT2fyrUaZFxrcVA= github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY= github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= github.com/leaanthony/typescriptify-golang-structs v0.1.7 h1:yoznzWzyxkO/iWdlpq+aPcuJ5Y/hpjq/lmgMFmpjwl0= github.com/leaanthony/typescriptify-golang-structs v0.1.7/go.mod h1:cWtOkiVhMF77e6phAXUcfNwYmMwCJ67Sij24lfvi9Js= -github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 h1:5iOd93PZbpH4Iir8QkC4coFD+zEQEZSIRcjwjTFZkr0= -github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18/go.mod h1:KEbMsKoznsebyGHwLk5LqkFOxL5uXSRdvpP4+avmAMs= github.com/leaanthony/winicon v1.0.0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= diff --git a/v2/cmd/wails/internal/commands/generate/template/base/go.tmpl.mod b/v2/cmd/wails/internal/commands/generate/template/base/go.tmpl.mod index 875bab063..d3dd6ecd2 100644 --- a/v2/cmd/wails/internal/commands/generate/template/base/go.tmpl.mod +++ b/v2/cmd/wails/internal/commands/generate/template/base/go.tmpl.mod @@ -1,37 +1,37 @@ module changeme -go 1.17 + go 1.17 -require github.com/wailsapp/wails/v2 {{.WailsVersion}} + require github.com/wailsapp/wails/v2 {{.WailsVersion}} -require ( -github.com/andybalholm/brotli v1.0.2 // indirect -github.com/davecgh/go-spew v1.1.1 // indirect -github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect -github.com/gabriel-vasile/mimetype v1.3.1 // indirect -github.com/go-ole/go-ole v1.2.5 // indirect -github.com/gofiber/fiber/v2 v2.17.0 // indirect -github.com/gofiber/websocket/v2 v2.0.8 // indirect -github.com/google/uuid v1.1.2 // indirect -github.com/imdario/mergo v0.3.12 // indirect -github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5 // indirect -github.com/klauspost/compress v1.12.2 // indirect -github.com/leaanthony/debme v1.2.1 // indirect -github.com/leaanthony/go-ansi-parser v1.0.1 // indirect -github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect -github.com/leaanthony/go-webview2 v0.0.0-20210914103035-f00aa774a934 // indirect -github.com/leaanthony/slicer v1.5.0 // indirect -github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect -github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect -github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect -github.com/pkg/errors v0.9.1 // indirect -github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect -github.com/tkrajina/go-reflector v0.5.5 // indirect -github.com/valyala/bytebufferpool v1.0.0 // indirect -github.com/valyala/fasthttp v1.28.0 // indirect -github.com/valyala/tcplisten v1.0.0 // indirect -golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect -) + require ( + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect + github.com/gabriel-vasile/mimetype v1.3.1 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/gofiber/fiber/v2 v2.17.0 // indirect + github.com/gofiber/websocket/v2 v2.0.8 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5 // indirect + github.com/klauspost/compress v1.12.2 // indirect + github.com/leaanthony/debme v1.2.1 // indirect + github.com/leaanthony/go-ansi-parser v1.0.1 // indirect + github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20210914103035-f00aa774a934 // indirect + github.com/leaanthony/slicer v1.5.0 // indirect + github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect + github.com/tkrajina/go-reflector v0.5.5 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.28.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect + golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect + ) -// replace github.com/wailsapp/wails/v2 {{.WailsVersion}} => {{.WailsDirectory}} \ No newline at end of file + // replace github.com/wailsapp/wails/v2 {{.WailsVersion}} => {{.WailsDirectory}} \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/go.mod.tmpl b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/go.mod.tmpl index 6c0aac70d..06f70133e 100644 --- a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/go.mod.tmpl +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/go.mod.tmpl @@ -19,10 +19,10 @@ github.com/klauspost/compress v1.12.2 // indirect github.com/leaanthony/debme v1.2.1 // indirect github.com/leaanthony/go-ansi-parser v1.0.1 // indirect github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect -github.com/leaanthony/go-webview2 v0.0.0-20210914103035-f00aa774a934 // indirect +github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20210914103035-f00aa774a934 // indirect github.com/leaanthony/slicer v1.5.0 // indirect github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect -github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect +github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/go.sum b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/go.sum index eb6e2e881..bba38583c 100644 --- a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/go.sum +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/go.sum @@ -83,16 +83,12 @@ github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQ github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM= github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y= github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0= -github.com/leaanthony/go-webview2 v0.0.0-20210928094513-a94a08b538bd h1:6m4zZ/esiByaDbzgdvDxjsOaIDgtuG1q2cyhjAi6uAg= -github.com/leaanthony/go-webview2 v0.0.0-20210928094513-a94a08b538bd/go.mod h1:lS5ds4bruPk9d7lzdF/OH31Z0YCerI6MmHNFGsWoUnM= github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4= github.com/leaanthony/idgen v1.0.0/go.mod h1:4nBZnt8ml/f/ic/EVQuLxuj817RccT2fyrUaZFxrcVA= github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY= github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= github.com/leaanthony/typescriptify-golang-structs v0.1.7 h1:yoznzWzyxkO/iWdlpq+aPcuJ5Y/hpjq/lmgMFmpjwl0= github.com/leaanthony/typescriptify-golang-structs v0.1.7/go.mod h1:cWtOkiVhMF77e6phAXUcfNwYmMwCJ67Sij24lfvi9Js= -github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 h1:5iOd93PZbpH4Iir8QkC4coFD+zEQEZSIRcjwjTFZkr0= -github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18/go.mod h1:KEbMsKoznsebyGHwLk5LqkFOxL5uXSRdvpP4+avmAMs= github.com/leaanthony/winicon v1.0.0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/go.mod.tmpl b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/go.mod.tmpl index 6c0aac70d..06f70133e 100644 --- a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/go.mod.tmpl +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/go.mod.tmpl @@ -19,10 +19,10 @@ github.com/klauspost/compress v1.12.2 // indirect github.com/leaanthony/debme v1.2.1 // indirect github.com/leaanthony/go-ansi-parser v1.0.1 // indirect github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect -github.com/leaanthony/go-webview2 v0.0.0-20210914103035-f00aa774a934 // indirect +github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20210914103035-f00aa774a934 // indirect github.com/leaanthony/slicer v1.5.0 // indirect github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect -github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect +github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/go.sum b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/go.sum index eb6e2e881..bba38583c 100644 --- a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/go.sum +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/go.sum @@ -83,16 +83,12 @@ github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQ github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM= github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y= github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0= -github.com/leaanthony/go-webview2 v0.0.0-20210928094513-a94a08b538bd h1:6m4zZ/esiByaDbzgdvDxjsOaIDgtuG1q2cyhjAi6uAg= -github.com/leaanthony/go-webview2 v0.0.0-20210928094513-a94a08b538bd/go.mod h1:lS5ds4bruPk9d7lzdF/OH31Z0YCerI6MmHNFGsWoUnM= github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4= github.com/leaanthony/idgen v1.0.0/go.mod h1:4nBZnt8ml/f/ic/EVQuLxuj817RccT2fyrUaZFxrcVA= github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY= github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= github.com/leaanthony/typescriptify-golang-structs v0.1.7 h1:yoznzWzyxkO/iWdlpq+aPcuJ5Y/hpjq/lmgMFmpjwl0= github.com/leaanthony/typescriptify-golang-structs v0.1.7/go.mod h1:cWtOkiVhMF77e6phAXUcfNwYmMwCJ67Sij24lfvi9Js= -github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 h1:5iOd93PZbpH4Iir8QkC4coFD+zEQEZSIRcjwjTFZkr0= -github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18/go.mod h1:KEbMsKoznsebyGHwLk5LqkFOxL5uXSRdvpP4+avmAMs= github.com/leaanthony/winicon v1.0.0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= diff --git a/v2/go.mod b/v2/go.mod index 14e1b5a3f..ce475f5b9 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -18,16 +18,15 @@ require ( github.com/gorilla/websocket v1.4.1 github.com/imdario/mergo v0.3.12 github.com/jackmordaunt/icns v1.0.0 + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e github.com/leaanthony/clir v1.0.4 github.com/leaanthony/debme v1.2.1 github.com/leaanthony/go-ansi-parser v1.0.1 github.com/leaanthony/go-common-file-dialog v1.0.3 - github.com/leaanthony/go-webview2 v1.0.3-0.20220314105146-f44268990abe github.com/leaanthony/gosod v1.0.3 github.com/leaanthony/idgen v1.0.0 github.com/leaanthony/slicer v1.5.0 github.com/leaanthony/typescriptify-golang-structs v0.1.7 - github.com/leaanthony/winc v0.0.0-20220323084916-ea5df694ec1f github.com/leaanthony/winicon v1.0.0 github.com/matryer/is v1.4.0 github.com/olekukonko/tablewriter v0.0.4 @@ -56,7 +55,6 @@ require ( github.com/go-ole/go-ole v1.2.6 // indirect github.com/google/go-cmp v0.5.5 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect github.com/klauspost/compress v1.12.2 // indirect github.com/kr/pretty v0.3.0 // indirect diff --git a/v2/go.sum b/v2/go.sum index ee11171e3..f408167df 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -88,7 +88,6 @@ github.com/jackmordaunt/icns v1.0.0 h1:RYSxplerf/l/DUd09AHtITwckkv/mqjVv4DjYdPmA github.com/jackmordaunt/icns v1.0.0/go.mod h1:7TTQVEuGzVVfOPPlLNHJIkzA6CoV7aH1Dv9dW351oOo= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= @@ -118,8 +117,6 @@ github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQ github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM= github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y= github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0= -github.com/leaanthony/go-webview2 v1.0.3-0.20220314105146-f44268990abe h1:8MRHsDSWiVHE5FIwyXKBTtdOGMQEOfmPNF1nGcbx3iY= -github.com/leaanthony/go-webview2 v1.0.3-0.20220314105146-f44268990abe/go.mod h1:iX54IaVk1FnDqMuHJ47VYLPQOcVqQiOe9SJACt9CAbU= github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ= github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4= github.com/leaanthony/idgen v1.0.0 h1:IZreR+JGEzFV4yeVuBZA25gM0keUoFy+RDUldncQ+Jw= @@ -128,10 +125,6 @@ github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0H github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= github.com/leaanthony/typescriptify-golang-structs v0.1.7 h1:yoznzWzyxkO/iWdlpq+aPcuJ5Y/hpjq/lmgMFmpjwl0= github.com/leaanthony/typescriptify-golang-structs v0.1.7/go.mod h1:cWtOkiVhMF77e6phAXUcfNwYmMwCJ67Sij24lfvi9Js= -github.com/leaanthony/winc v0.0.0-20220208061147-37b059b9dc3b h1:cJ+VfVwX3GkRGSy0SiOyZ7FjSGMPAY/rS/wJzilo23I= -github.com/leaanthony/winc v0.0.0-20220208061147-37b059b9dc3b/go.mod h1:OPfk8SNMAKRcSv8Vw1QL0yupmwcRtJyXZUgtMoaHUGc= -github.com/leaanthony/winc v0.0.0-20220323084916-ea5df694ec1f h1:RM0TNQXGTt06ZrSysdo+r9E9fk1ObACFBOww+W1zOiU= -github.com/leaanthony/winc v0.0.0-20220323084916-ea5df694ec1f/go.mod h1:OPfk8SNMAKRcSv8Vw1QL0yupmwcRtJyXZUgtMoaHUGc= github.com/leaanthony/winicon v1.0.0 h1:ZNt5U5dY71oEoKZ97UVwJRT4e+5xo5o/ieKuHuk8NqQ= github.com/leaanthony/winicon v1.0.0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= @@ -253,7 +246,6 @@ golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210218145245-beda7e5e158e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/v2/internal/appng/app_bindings.go b/v2/internal/appng/app_bindings.go index 563dff791..6f94e211f 100644 --- a/v2/internal/appng/app_bindings.go +++ b/v2/internal/appng/app_bindings.go @@ -98,7 +98,7 @@ func generateBindings(bindings *binding.Bindings) error { // Write backend method wrappers bindingsFilename := filepath.Join(targetDir, "bindings.js") - err = bindings.GenerateBackendJS(bindingsFilename, true) + err = bindings.GenerateBackendJS(bindingsFilename) if err != nil { return err } diff --git a/v2/internal/appng/app_default_windows.go b/v2/internal/appng/app_default_windows.go index 59174e834..cf70cef90 100644 --- a/v2/internal/appng/app_default_windows.go +++ b/v2/internal/appng/app_default_windows.go @@ -6,7 +6,7 @@ package appng import ( "os/exec" - "github.com/leaanthony/winc/w32" + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" "github.com/wailsapp/wails/v2/pkg/options" ) diff --git a/v2/internal/appng/app_dev.go b/v2/internal/appng/app_dev.go index 7069ccaa2..f47b25bb6 100644 --- a/v2/internal/appng/app_dev.go +++ b/v2/internal/appng/app_dev.go @@ -211,7 +211,7 @@ func generateBindings(bindings *binding.Bindings) error { // Write backend method wrappers bindingsFilename := filepath.Join(targetDir, "bindings.js") - err = bindings.GenerateBackendJS(bindingsFilename, false) + err = bindings.GenerateBackendJS(bindingsFilename) if err != nil { return err } diff --git a/v2/internal/binding/generate.go b/v2/internal/binding/generate.go index f98daf965..1d0a1b186 100644 --- a/v2/internal/binding/generate.go +++ b/v2/internal/binding/generate.go @@ -16,7 +16,7 @@ import ( //go:embed assets/package.json var packageJSON []byte -func (b *Bindings) GenerateBackendJS(targetfile string, isDevBindings bool) error { +func (b *Bindings) GenerateBackendJS(targetfile string) error { store := b.db.store var output bytes.Buffer @@ -26,15 +26,6 @@ func (b *Bindings) GenerateBackendJS(targetfile string, isDevBindings bool) erro // This file is automatically generated. DO NOT EDIT `) - if isDevBindings { - json, err := b.ToJSON() - if err != nil { - return err - } - output.WriteString("window.wailsbindings = " + json + ";") - output.WriteString("\n") - } - output.WriteString(`const go = {`) output.WriteString("\n") diff --git a/v2/internal/ffenestri/windows/wv2runtime/wv2runtime.go b/v2/internal/ffenestri/windows/wv2runtime/wv2runtime.go index 108386c10..7b9f9592d 100644 --- a/v2/internal/ffenestri/windows/wv2runtime/wv2runtime.go +++ b/v2/internal/ffenestri/windows/wv2runtime/wv2runtime.go @@ -1,7 +1,7 @@ package wv2runtime import ( - "github.com/leaanthony/go-webview2/webviewloader" + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/webviewloader" ) const MinimumRuntimeVersion string = "91.0.992.28" diff --git a/v2/internal/frontend/desktop/windows/frontend.go b/v2/internal/frontend/desktop/windows/frontend.go index e8ddf4ab0..d8d6a3adf 100644 --- a/v2/internal/frontend/desktop/windows/frontend.go +++ b/v2/internal/frontend/desktop/windows/frontend.go @@ -14,13 +14,13 @@ import ( "strings" "text/template" - "github.com/leaanthony/go-webview2/pkg/edge" - "github.com/leaanthony/winc" - "github.com/leaanthony/winc/w32" "github.com/wailsapp/wails/v2/internal/binding" "github.com/wailsapp/wails/v2/internal/frontend" "github.com/wailsapp/wails/v2/internal/frontend/assetserver" "github.com/wailsapp/wails/v2/internal/frontend/desktop/common" + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge" + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc" + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" "github.com/wailsapp/wails/v2/internal/logger" "github.com/wailsapp/wails/v2/pkg/options" ) diff --git a/v2/internal/frontend/desktop/windows/go-webview2/LICENSE b/v2/internal/frontend/desktop/windows/go-webview2/LICENSE new file mode 100644 index 000000000..ef2a0f485 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2020 John Chadwick +Some portions Copyright (c) 2017 Serge Zaitsev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/v2/internal/frontend/desktop/windows/go-webview2/README.md b/v2/internal/frontend/desktop/windows/go-webview2/README.md new file mode 100644 index 000000000..5555fa619 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/README.md @@ -0,0 +1,27 @@ +# go-webview2 + +This is a proof of concept for embedding Webview2 into Go without CGo. It is based +on [webview/webview](https://github.com/webview/webview) and provides a compatible API. + +## Notice + +Because this version doesn't currently have an EdgeHTML fallback, it will not work unless you have a Webview2 runtime +installed. In addition, it requires the Webview2Loader DLL in order to function. Adding an EdgeHTML fallback should be +technically possible but will likely require much worse hacks since the API is not strictly COM to my knowledge. + +## Demo + +For now, you'll need to install the Webview2 runtime, as it does not ship with Windows. + +[WebView2 runtime](https://developer.microsoft.com/en-us/microsoft-edge/webview2/) + +After that, you should be able to run go-webview2 directly: + +``` +go run github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/cmd/demo +``` + +This will use go-winloader to load an embedded copy of WebView2Loader.dll. + +If this does not work, please try running from a directory that has an appropriate copy of `WebView2Loader.dll` for your +GOARCH. If _that_ worked, *please* file a bug so we can figure out what's wrong with go-winloader :) \ No newline at end of file diff --git a/v2/internal/frontend/desktop/windows/go-webview2/internal/w32/w32.go b/v2/internal/frontend/desktop/windows/go-webview2/internal/w32/w32.go new file mode 100644 index 000000000..cfd1bfbda --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/internal/w32/w32.go @@ -0,0 +1,154 @@ +package w32 + +import ( + "golang.org/x/sys/windows" + "syscall" + "unicode/utf16" + "unsafe" +) + +var ( + ole32 = windows.NewLazySystemDLL("ole32") + Ole32CoInitializeEx = ole32.NewProc("CoInitializeEx") + + kernel32 = windows.NewLazySystemDLL("kernel32") + Kernel32GetCurrentThreadID = kernel32.NewProc("GetCurrentThreadId") + + shlwapi = windows.NewLazySystemDLL("shlwapi") + shlwapiSHCreateMemStream = shlwapi.NewProc("SHCreateMemStream") + + user32 = windows.NewLazySystemDLL("user32") + User32LoadImageW = user32.NewProc("LoadImageW") + User32GetSystemMetrics = user32.NewProc("GetSystemMetrics") + User32RegisterClassExW = user32.NewProc("RegisterClassExW") + User32CreateWindowExW = user32.NewProc("CreateWindowExW") + User32DestroyWindow = user32.NewProc("DestroyWindow") + User32ShowWindow = user32.NewProc("ShowWindow") + User32UpdateWindow = user32.NewProc("UpdateWindow") + User32SetFocus = user32.NewProc("SetFocus") + User32GetMessageW = user32.NewProc("GetMessageW") + User32TranslateMessage = user32.NewProc("TranslateMessage") + User32DispatchMessageW = user32.NewProc("DispatchMessageW") + User32DefWindowProcW = user32.NewProc("DefWindowProcW") + User32GetClientRect = user32.NewProc("GetClientRect") + User32PostQuitMessage = user32.NewProc("PostQuitMessage") + User32SetWindowTextW = user32.NewProc("SetWindowTextW") + User32PostThreadMessageW = user32.NewProc("PostThreadMessageW") + User32GetWindowLongPtrW = user32.NewProc("GetWindowLongPtrW") + User32SetWindowLongPtrW = user32.NewProc("SetWindowLongPtrW") + User32AdjustWindowRect = user32.NewProc("AdjustWindowRect") + User32SetWindowPos = user32.NewProc("SetWindowPos") +) + +const ( + SystemMetricsCxIcon = 11 + SystemMetricsCyIcon = 12 +) + +const ( + SWShow = 5 +) + +const ( + SWPNoZOrder = 0x0004 + SWPNoActivate = 0x0010 + SWPNoMove = 0x0002 + SWPFrameChanged = 0x0020 +) + +const ( + WMDestroy = 0x0002 + WMMove = 0x0003 + WMSize = 0x0005 + WMClose = 0x0010 + WMQuit = 0x0012 + WMGetMinMaxInfo = 0x0024 + WMNCLButtonDown = 0x00A1 + WMMoving = 0x0216 + WMApp = 0x8000 +) + +const ( + GWLStyle = -16 +) + +const ( + WSOverlapped = 0x00000000 + WSMaximizeBox = 0x00020000 + WSThickFrame = 0x00040000 + WSCaption = 0x00C00000 + WSSysMenu = 0x00080000 + WSMinimizeBox = 0x00020000 + WSOverlappedWindow = (WSOverlapped | WSCaption | WSSysMenu | WSThickFrame | WSMinimizeBox | WSMaximizeBox) +) + +type WndClassExW struct { + CbSize uint32 + Style uint32 + LpfnWndProc uintptr + CnClsExtra int32 + CbWndExtra int32 + HInstance windows.Handle + HIcon windows.Handle + HCursor windows.Handle + HbrBackground windows.Handle + LpszMenuName *uint16 + LpszClassName *uint16 + HIconSm windows.Handle +} + +type Rect struct { + Left int32 + Top int32 + Right int32 + Bottom int32 +} + +type MinMaxInfo struct { + PtReserved Point + PtMaxSize Point + PtMaxPosition Point + PtMinTrackSize Point + PtMaxTrackSize Point +} + +type Point struct { + X, Y int32 +} + +type Msg struct { + Hwnd syscall.Handle + Message uint32 + WParam uintptr + LParam uintptr + Time uint32 + Pt Point + LPrivate uint32 +} + +func Utf16PtrToString(p *uint16) string { + if p == nil { + return "" + } + // Find NUL terminator. + end := unsafe.Pointer(p) + n := 0 + for *(*uint16)(end) != 0 { + end = unsafe.Pointer(uintptr(end) + unsafe.Sizeof(*p)) + n++ + } + s := (*[(1 << 30) - 1]uint16)(unsafe.Pointer(p))[:n:n] + return string(utf16.Decode(s)) +} + +func SHCreateMemStream(data []byte) (uintptr, error) { + ret, _, err := shlwapiSHCreateMemStream.Call( + uintptr(unsafe.Pointer(&data[0])), + uintptr(len(data)), + ) + if ret == 0 { + return 0, err + } + + return ret, nil +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_COLOR.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_COLOR.go new file mode 100644 index 000000000..43eb12e0b --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_COLOR.go @@ -0,0 +1,8 @@ +package edge + +type COREWEBVIEW2_COLOR struct { + A uint8 + R uint8 + G uint8 + B uint8 +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND.go new file mode 100644 index 000000000..6a378fcb8 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND.go @@ -0,0 +1,9 @@ +package edge + +type COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND uint32 + +const ( + COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY = iota + COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_ALLOW + COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY_CORS +) diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_KEY_EVENT_KIND.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_KEY_EVENT_KIND.go new file mode 100644 index 000000000..0b5715887 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_KEY_EVENT_KIND.go @@ -0,0 +1,10 @@ +package edge + +type COREWEBVIEW2_KEY_EVENT_KIND uint32 + +const ( + COREWEBVIEW2_KEY_EVENT_KIND_KEY_DOWN = 0 + COREWEBVIEW2_KEY_EVENT_KIND_KEY_UP = 1 + COREWEBVIEW2_KEY_EVENT_KIND_SYSTEM_KEY_DOWN = 2 + COREWEBVIEW2_KEY_EVENT_KIND_SYSTEM_KEY_UP = 3 +) diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_MOVE_FOCUS_REASON.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_MOVE_FOCUS_REASON.go new file mode 100644 index 000000000..e901101ef --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_MOVE_FOCUS_REASON.go @@ -0,0 +1,9 @@ +package edge + +type COREWEBVIEW2_MOVE_FOCUS_REASON uint32 + +const ( + COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC = 0 + COREWEBVIEW2_MOVE_FOCUS_REASON_NEXT = 1 + COREWEBVIEW2_MOVE_FOCUS_REASON_PREVIOUS = 2 +) diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_PHYSICAL_KEY_STATUS.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_PHYSICAL_KEY_STATUS.go new file mode 100644 index 000000000..8fc93e0e5 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_PHYSICAL_KEY_STATUS.go @@ -0,0 +1,10 @@ +package edge + +type COREWEBVIEW2_PHYSICAL_KEY_STATUS struct { + RepeatCount uint32 + ScanCode uint32 + IsExtendedKey bool + IsMenuKeyDown bool + WasKeyDown bool + IsKeyReleased bool +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_WEB_RESOURCE_CONTEXT.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_WEB_RESOURCE_CONTEXT.go new file mode 100644 index 000000000..9ca9c41cf --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/COREWEBVIEW2_WEB_RESOURCE_CONTEXT.go @@ -0,0 +1,23 @@ +package edge + +type COREWEBVIEW2_WEB_RESOURCE_CONTEXT uint32 + +const ( + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL = 0 + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_DOCUMENT = 1 + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_STYLESHEET = 2 + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_IMAGE = 3 + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_MEDIA = 4 + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_FONT = 5 + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_SCRIPT = 6 + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_XML_HTTP_REQUEST = 7 + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_FETCH = 8 + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_TEXT_TRACK = 9 + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_EVENT_SOURCE = 10 + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_WEBSOCKET = 11 + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_MANIFEST = 12 + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_SIGNED_EXCHANGE = 13 + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_PING = 14 + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_CSP_VIOLATION_REPORT = 15 + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_OTHER = 16 +) diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2AcceleratorKeyPressedEventArgs.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2AcceleratorKeyPressedEventArgs.go new file mode 100644 index 000000000..e3ff7bb53 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2AcceleratorKeyPressedEventArgs.go @@ -0,0 +1,76 @@ +package edge + +import ( + "golang.org/x/sys/windows" + "unsafe" +) + +type _ICoreWebView2AcceleratorKeyPressedEventArgsVtbl struct { + _IUnknownVtbl + GetKeyEventKind ComProc + GetVirtualKey ComProc + GetKeyEventLParam ComProc + GetPhysicalKeyStatus ComProc + GetHandled ComProc + PutHandled ComProc +} + +type ICoreWebView2AcceleratorKeyPressedEventArgs struct { + vtbl *_ICoreWebView2AcceleratorKeyPressedEventArgsVtbl +} + +func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) AddRef() uintptr { + return i.AddRef() +} + +func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) GetKeyEventKind() (COREWEBVIEW2_KEY_EVENT_KIND, error) { + var err error + var keyEventKind COREWEBVIEW2_KEY_EVENT_KIND + _, _, err = i.vtbl.GetKeyEventKind.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&keyEventKind)), + ) + if err != windows.ERROR_SUCCESS { + return 0, err + } + return keyEventKind, nil +} + +func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) GetVirtualKey() (uint, error) { + var err error + var virtualKey uint + _, _, err = i.vtbl.GetVirtualKey.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&virtualKey)), + ) + if err != windows.ERROR_SUCCESS { + return 0, err + } + return virtualKey, nil +} + +func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) GetPhysicalKeyStatus() (COREWEBVIEW2_PHYSICAL_KEY_STATUS, error) { + var err error + var physicalKeyStatus COREWEBVIEW2_PHYSICAL_KEY_STATUS + _, _, err = i.vtbl.GetPhysicalKeyStatus.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&physicalKeyStatus)), + ) + if err != windows.ERROR_SUCCESS { + return COREWEBVIEW2_PHYSICAL_KEY_STATUS{}, err + } + return physicalKeyStatus, nil +} + +func (i *ICoreWebView2AcceleratorKeyPressedEventArgs) PutHandled(handled bool) error { + var err error + + _, _, err = i.vtbl.PutHandled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(handled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2AcceleratorKeyPressedEventHandler.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2AcceleratorKeyPressedEventHandler.go new file mode 100644 index 000000000..093cc8044 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2AcceleratorKeyPressedEventHandler.go @@ -0,0 +1,51 @@ +package edge + +type _ICoreWebView2AcceleratorKeyPressedEventHandlerVtbl struct { + _IUnknownVtbl + Invoke ComProc +} + +type ICoreWebView2AcceleratorKeyPressedEventHandler struct { + vtbl *_ICoreWebView2AcceleratorKeyPressedEventHandlerVtbl + impl _ICoreWebView2AcceleratorKeyPressedEventHandlerImpl +} + +func (i *ICoreWebView2AcceleratorKeyPressedEventHandler) AddRef() uintptr { + return i.AddRef() +} +func _ICoreWebView2AcceleratorKeyPressedEventHandlerIUnknownQueryInterface(this *ICoreWebView2AcceleratorKeyPressedEventHandler, refiid, object uintptr) uintptr { + return this.impl.QueryInterface(refiid, object) +} + +func _ICoreWebView2AcceleratorKeyPressedEventHandlerIUnknownAddRef(this *ICoreWebView2AcceleratorKeyPressedEventHandler) uintptr { + return this.impl.AddRef() +} + +func _ICoreWebView2AcceleratorKeyPressedEventHandlerIUnknownRelease(this *ICoreWebView2AcceleratorKeyPressedEventHandler) uintptr { + return this.impl.Release() +} + +func _ICoreWebView2AcceleratorKeyPressedEventHandlerInvoke(this *ICoreWebView2AcceleratorKeyPressedEventHandler, sender *ICoreWebView2Controller, args *ICoreWebView2AcceleratorKeyPressedEventArgs) uintptr { + return this.impl.AcceleratorKeyPressed(sender, args) +} + +type _ICoreWebView2AcceleratorKeyPressedEventHandlerImpl interface { + _IUnknownImpl + AcceleratorKeyPressed(sender *ICoreWebView2Controller, args *ICoreWebView2AcceleratorKeyPressedEventArgs) uintptr +} + +var _ICoreWebView2AcceleratorKeyPressedEventHandlerFn = _ICoreWebView2AcceleratorKeyPressedEventHandlerVtbl{ + _IUnknownVtbl{ + NewComProc(_ICoreWebView2AcceleratorKeyPressedEventHandlerIUnknownQueryInterface), + NewComProc(_ICoreWebView2AcceleratorKeyPressedEventHandlerIUnknownAddRef), + NewComProc(_ICoreWebView2AcceleratorKeyPressedEventHandlerIUnknownRelease), + }, + NewComProc(_ICoreWebView2AcceleratorKeyPressedEventHandlerInvoke), +} + +func newICoreWebView2AcceleratorKeyPressedEventHandler(impl _ICoreWebView2AcceleratorKeyPressedEventHandlerImpl) *ICoreWebView2AcceleratorKeyPressedEventHandler { + return &ICoreWebView2AcceleratorKeyPressedEventHandler{ + vtbl: &_ICoreWebView2AcceleratorKeyPressedEventHandlerFn, + impl: impl, + } +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2Controller.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2Controller.go new file mode 100644 index 000000000..8ecdd57e4 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2Controller.go @@ -0,0 +1,132 @@ +package edge + +import ( + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/internal/w32" + "golang.org/x/sys/windows" +) + +type _ICoreWebView2ControllerVtbl struct { + _IUnknownVtbl + GetIsVisible ComProc + PutIsVisible ComProc + GetBounds ComProc + PutBounds ComProc + GetZoomFactor ComProc + PutZoomFactor ComProc + AddZoomFactorChanged ComProc + RemoveZoomFactorChanged ComProc + SetBoundsAndZoomFactor ComProc + MoveFocus ComProc + AddMoveFocusRequested ComProc + RemoveMoveFocusRequested ComProc + AddGotFocus ComProc + RemoveGotFocus ComProc + AddLostFocus ComProc + RemoveLostFocus ComProc + AddAcceleratorKeyPressed ComProc + RemoveAcceleratorKeyPressed ComProc + GetParentWindow ComProc + PutParentWindow ComProc + NotifyParentWindowPositionChanged ComProc + Close ComProc + GetCoreWebView2 ComProc +} + +type ICoreWebView2Controller struct { + vtbl *_ICoreWebView2ControllerVtbl +} + +func (i *ICoreWebView2Controller) AddRef() uintptr { + return i.AddRef() +} + +func (i *ICoreWebView2Controller) GetBounds() (*w32.Rect, error) { + var err error + var bounds w32.Rect + _, _, err = i.vtbl.GetBounds.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&bounds)), + ) + if err != windows.ERROR_SUCCESS { + return nil, err + } + return &bounds, nil +} + +func (i *ICoreWebView2Controller) PutBounds(bounds w32.Rect) error { + var err error + + _, _, err = i.vtbl.PutBounds.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&bounds)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebView2Controller) MoveFocus(reason COREWEBVIEW2_MOVE_FOCUS_REASON) error { + var err error + + _, _, err = i.vtbl.MoveFocus.Call( + uintptr(unsafe.Pointer(i)), + uintptr(reason), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebView2Controller) AddAcceleratorKeyPressed(eventHandler *ICoreWebView2AcceleratorKeyPressedEventHandler, token *_EventRegistrationToken) error { + var err error + _, _, err = i.vtbl.AddAcceleratorKeyPressed.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(eventHandler)), + uintptr(unsafe.Pointer(&token)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebView2Controller) PutIsVisible(isVisible bool) error { + var err error + + _, _, err = i.vtbl.PutIsVisible.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(isVisible)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebView2Controller) GetICoreWebView2Controller2() *ICoreWebView2Controller2 { + + var result *ICoreWebView2Controller2 + + iidICoreWebView2Controller2 := NewGUID("{c979903e-d4ca-4228-92eb-47ee3fa96eab}") + i.vtbl.QueryInterface.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(iidICoreWebView2Controller2)), + uintptr(unsafe.Pointer(&result))) + + return result +} + +func (i *ICoreWebView2Controller) NotifyParentWindowPositionChanged() error { + var err error + _, _, err = i.vtbl.NotifyParentWindowPositionChanged.Call( + uintptr(unsafe.Pointer(i)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2Controller2.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2Controller2.go new file mode 100644 index 000000000..26835495c --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2Controller2.go @@ -0,0 +1,72 @@ +package edge + +import ( + "golang.org/x/sys/windows" + "unsafe" +) + +type _ICoreWebView2Controller2Vtbl struct { + _IUnknownVtbl + GetIsVisible ComProc + PutIsVisible ComProc + GetBounds ComProc + PutBounds ComProc + GetZoomFactor ComProc + PutZoomFactor ComProc + AddZoomFactorChanged ComProc + RemoveZoomFactorChanged ComProc + SetBoundsAndZoomFactor ComProc + MoveFocus ComProc + AddMoveFocusRequested ComProc + RemoveMoveFocusRequested ComProc + AddGotFocus ComProc + RemoveGotFocus ComProc + AddLostFocus ComProc + RemoveLostFocus ComProc + AddAcceleratorKeyPressed ComProc + RemoveAcceleratorKeyPressed ComProc + GetParentWindow ComProc + PutParentWindow ComProc + NotifyParentWindowPositionChanged ComProc + Close ComProc + GetCoreWebView2 ComProc + GetDefaultBackgroundColor ComProc + PutDefaultBackgroundColor ComProc +} + +type ICoreWebView2Controller2 struct { + vtbl *_ICoreWebView2Controller2Vtbl +} + +func (i *ICoreWebView2Controller2) AddRef() uintptr { + return i.AddRef() +} + +func (i *ICoreWebView2Controller2) GetDefaultBackgroundColor() (*COREWEBVIEW2_COLOR, error) { + var err error + var backgroundColor *COREWEBVIEW2_COLOR + _, _, err = i.vtbl.GetDefaultBackgroundColor.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&backgroundColor)), + ) + if err != windows.ERROR_SUCCESS { + return nil, err + } + return backgroundColor, nil +} + +func (i *ICoreWebView2Controller2) PutDefaultBackgroundColor(backgroundColor COREWEBVIEW2_COLOR) error { + var err error + + // Cast to a uint32 as that's what the call is expecting + col := *(*uint32)(unsafe.Pointer(&backgroundColor)) + + _, _, err = i.vtbl.PutDefaultBackgroundColor.Call( + uintptr(unsafe.Pointer(i)), + uintptr(col), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2CreateCoreWebView2ControllerCompletedHandler.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2CreateCoreWebView2ControllerCompletedHandler.go new file mode 100644 index 000000000..ca92d34b3 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2CreateCoreWebView2ControllerCompletedHandler.go @@ -0,0 +1,51 @@ +package edge + +type _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl struct { + _IUnknownVtbl + Invoke ComProc +} + +type iCoreWebView2CreateCoreWebView2ControllerCompletedHandler struct { + vtbl *_ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl + impl _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerImpl +} + +func (i *iCoreWebView2CreateCoreWebView2ControllerCompletedHandler) AddRef() uintptr { + return i.AddRef() +} +func _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerIUnknownQueryInterface(this *iCoreWebView2CreateCoreWebView2ControllerCompletedHandler, refiid, object uintptr) uintptr { + return this.impl.QueryInterface(refiid, object) +} + +func _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerIUnknownAddRef(this *iCoreWebView2CreateCoreWebView2ControllerCompletedHandler) uintptr { + return this.impl.AddRef() +} + +func _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerIUnknownRelease(this *iCoreWebView2CreateCoreWebView2ControllerCompletedHandler) uintptr { + return this.impl.Release() +} + +func _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerInvoke(this *iCoreWebView2CreateCoreWebView2ControllerCompletedHandler, errorCode uintptr, createdController *ICoreWebView2Controller) uintptr { + return this.impl.CreateCoreWebView2ControllerCompleted(errorCode, createdController) +} + +type _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerImpl interface { + _IUnknownImpl + CreateCoreWebView2ControllerCompleted(errorCode uintptr, createdController *ICoreWebView2Controller) uintptr +} + +var _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerFn = _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl{ + _IUnknownVtbl{ + NewComProc(_ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerIUnknownQueryInterface), + NewComProc(_ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerIUnknownAddRef), + NewComProc(_ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerIUnknownRelease), + }, + NewComProc(_ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerInvoke), +} + +func newICoreWebView2CreateCoreWebView2ControllerCompletedHandler(impl _ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerImpl) *iCoreWebView2CreateCoreWebView2ControllerCompletedHandler { + return &iCoreWebView2CreateCoreWebView2ControllerCompletedHandler{ + vtbl: &_ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerFn, + impl: impl, + } +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2NavigationCompletedEventArgs.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2NavigationCompletedEventArgs.go new file mode 100644 index 000000000..e304bb9d3 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2NavigationCompletedEventArgs.go @@ -0,0 +1,16 @@ +package edge + +type _ICoreWebView2NavigationCompletedEventArgsVtbl struct { + _IUnknownVtbl + GetIsSuccess ComProc + GetWebErrorStatus ComProc + GetNavigationId ComProc +} + +type ICoreWebView2NavigationCompletedEventArgs struct { + vtbl *_ICoreWebView2NavigationCompletedEventArgsVtbl +} + +func (i *ICoreWebView2NavigationCompletedEventArgs) AddRef() uintptr { + return i.AddRef() +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2NavigationCompletedEventHandler.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2NavigationCompletedEventHandler.go new file mode 100644 index 000000000..6fbfe724c --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2NavigationCompletedEventHandler.go @@ -0,0 +1,51 @@ +package edge + +type _ICoreWebView2NavigationCompletedEventHandlerVtbl struct { + _IUnknownVtbl + Invoke ComProc +} + +type ICoreWebView2NavigationCompletedEventHandler struct { + vtbl *_ICoreWebView2NavigationCompletedEventHandlerVtbl + impl _ICoreWebView2NavigationCompletedEventHandlerImpl +} + +func (i *ICoreWebView2NavigationCompletedEventHandler) AddRef() uintptr { + return i.AddRef() +} +func _ICoreWebView2NavigationCompletedEventHandlerIUnknownQueryInterface(this *ICoreWebView2NavigationCompletedEventHandler, refiid, object uintptr) uintptr { + return this.impl.QueryInterface(refiid, object) +} + +func _ICoreWebView2NavigationCompletedEventHandlerIUnknownAddRef(this *ICoreWebView2NavigationCompletedEventHandler) uintptr { + return this.impl.AddRef() +} + +func _ICoreWebView2NavigationCompletedEventHandlerIUnknownRelease(this *ICoreWebView2NavigationCompletedEventHandler) uintptr { + return this.impl.Release() +} + +func _ICoreWebView2NavigationCompletedEventHandlerInvoke(this *ICoreWebView2NavigationCompletedEventHandler, sender *ICoreWebView2, args *ICoreWebView2NavigationCompletedEventArgs) uintptr { + return this.impl.NavigationCompleted(sender, args) +} + +type _ICoreWebView2NavigationCompletedEventHandlerImpl interface { + _IUnknownImpl + NavigationCompleted(sender *ICoreWebView2, args *ICoreWebView2NavigationCompletedEventArgs) uintptr +} + +var _ICoreWebView2NavigationCompletedEventHandlerFn = _ICoreWebView2NavigationCompletedEventHandlerVtbl{ + _IUnknownVtbl{ + NewComProc(_ICoreWebView2NavigationCompletedEventHandlerIUnknownQueryInterface), + NewComProc(_ICoreWebView2NavigationCompletedEventHandlerIUnknownAddRef), + NewComProc(_ICoreWebView2NavigationCompletedEventHandlerIUnknownRelease), + }, + NewComProc(_ICoreWebView2NavigationCompletedEventHandlerInvoke), +} + +func newICoreWebView2NavigationCompletedEventHandler(impl _ICoreWebView2NavigationCompletedEventHandlerImpl) *ICoreWebView2NavigationCompletedEventHandler { + return &ICoreWebView2NavigationCompletedEventHandler{ + vtbl: &_ICoreWebView2NavigationCompletedEventHandlerFn, + impl: impl, + } +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2Settings.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2Settings.go new file mode 100644 index 000000000..7f6e1b9b5 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2Settings.go @@ -0,0 +1,268 @@ +package edge + +import ( + "golang.org/x/sys/windows" + "unsafe" +) + +type _ICoreWebView2SettingsVtbl struct { + _IUnknownVtbl + GetIsScriptEnabled ComProc + PutIsScriptEnabled ComProc + GetIsWebMessageEnabled ComProc + PutIsWebMessageEnabled ComProc + GetAreDefaultScriptDialogsEnabled ComProc + PutAreDefaultScriptDialogsEnabled ComProc + GetIsStatusBarEnabled ComProc + PutIsStatusBarEnabled ComProc + GetAreDevToolsEnabled ComProc + PutAreDevToolsEnabled ComProc + GetAreDefaultContextMenusEnabled ComProc + PutAreDefaultContextMenusEnabled ComProc + GetAreHostObjectsAllowed ComProc + PutAreHostObjectsAllowed ComProc + GetIsZoomControlEnabled ComProc + PutIsZoomControlEnabled ComProc + GetIsBuiltInErrorPageEnabled ComProc + PutIsBuiltInErrorPageEnabled ComProc +} + +type ICoreWebView2Settings struct { + vtbl *_ICoreWebView2SettingsVtbl +} + +func (i *ICoreWebView2Settings) AddRef() uintptr { + return i.AddRef() +} + +func (i *ICoreWebView2Settings) GetIsScriptEnabled() (bool, error) { + var err error + var isScriptEnabled bool + _, _, err = i.vtbl.GetIsScriptEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&isScriptEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return isScriptEnabled, nil +} + +func (i *ICoreWebView2Settings) PutIsScriptEnabled(isScriptEnabled bool) error { + var err error + + _, _, err = i.vtbl.PutIsScriptEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(isScriptEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebView2Settings) GetIsWebMessageEnabled() (bool, error) { + var err error + var isWebMessageEnabled bool + _, _, err = i.vtbl.GetIsWebMessageEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&isWebMessageEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return isWebMessageEnabled, nil +} + +func (i *ICoreWebView2Settings) PutIsWebMessageEnabled(isWebMessageEnabled bool) error { + var err error + + _, _, err = i.vtbl.PutIsWebMessageEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(isWebMessageEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebView2Settings) GetAreDefaultScriptDialogsEnabled() (bool, error) { + var err error + var areDefaultScriptDialogsEnabled bool + _, _, err = i.vtbl.GetAreDefaultScriptDialogsEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&areDefaultScriptDialogsEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return areDefaultScriptDialogsEnabled, nil +} + +func (i *ICoreWebView2Settings) PutAreDefaultScriptDialogsEnabled(areDefaultScriptDialogsEnabled bool) error { + var err error + + _, _, err = i.vtbl.PutAreDefaultScriptDialogsEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(areDefaultScriptDialogsEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebView2Settings) GetIsStatusBarEnabled() (bool, error) { + var err error + var isStatusBarEnabled bool + _, _, err = i.vtbl.GetIsStatusBarEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&isStatusBarEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return isStatusBarEnabled, nil +} + +func (i *ICoreWebView2Settings) PutIsStatusBarEnabled(isStatusBarEnabled bool) error { + var err error + + _, _, err = i.vtbl.PutIsStatusBarEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(isStatusBarEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebView2Settings) GetAreDevToolsEnabled() (bool, error) { + var err error + var areDevToolsEnabled bool + _, _, err = i.vtbl.GetAreDevToolsEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&areDevToolsEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return areDevToolsEnabled, nil +} + +func (i *ICoreWebView2Settings) PutAreDevToolsEnabled(areDevToolsEnabled bool) error { + var err error + _, _, err = i.vtbl.PutAreDevToolsEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(areDevToolsEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebView2Settings) GetAreDefaultContextMenusEnabled() (bool, error) { + var err error + var enabled bool + _, _, err = i.vtbl.GetAreDefaultContextMenusEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&enabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return enabled, nil +} + +func (i *ICoreWebView2Settings) PutAreDefaultContextMenusEnabled(enabled bool) error { + var err error + _, _, err = i.vtbl.PutAreDefaultContextMenusEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(enabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebView2Settings) GetAreHostObjectsAllowed() (bool, error) { + var err error + var allowed bool + _, _, err = i.vtbl.GetAreHostObjectsAllowed.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&allowed)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return allowed, nil +} + +func (i *ICoreWebView2Settings) PutAreHostObjectsAllowed(allowed bool) error { + var err error + + _, _, err = i.vtbl.PutAreHostObjectsAllowed.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(allowed)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebView2Settings) GetIsZoomControlEnabled() (bool, error) { + var err error + var enabled bool + _, _, err = i.vtbl.GetIsZoomControlEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&enabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return enabled, nil +} + +func (i *ICoreWebView2Settings) PutIsZoomControlEnabled(enabled bool) error { + var err error + + _, _, err = i.vtbl.PutIsZoomControlEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(enabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebView2Settings) GetIsBuiltInErrorPageEnabled() (bool, error) { + var err error + var enabled bool + _, _, err = i.vtbl.GetIsBuiltInErrorPageEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&enabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return enabled, nil +} + +func (i *ICoreWebView2Settings) PutIsBuiltInErrorPageEnabled(enabled bool) error { + var err error + + _, _, err = i.vtbl.PutIsBuiltInErrorPageEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(enabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2WebResourceRequest.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2WebResourceRequest.go new file mode 100644 index 000000000..06a9671cf --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2WebResourceRequest.go @@ -0,0 +1,46 @@ +package edge + +import ( + "unsafe" + + "golang.org/x/sys/windows" +) + +type _ICoreWebView2WebResourceRequestVtbl struct { + _IUnknownVtbl + GetUri ComProc + PutUri ComProc + GetMethod ComProc + PutMethod ComProc + GetContent ComProc + PutContent ComProc + GetHeaders ComProc +} + +type ICoreWebView2WebResourceRequest struct { + vtbl *_ICoreWebView2WebResourceRequestVtbl +} + +func (i *ICoreWebView2WebResourceRequest) AddRef() uintptr { + return i.AddRef() +} + +func (i *ICoreWebView2WebResourceRequest) GetUri() (string, error) { + var err error + // Create *uint16 to hold result + var _uri *uint16 + _, _, err = i.vtbl.GetUri.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&_uri)), + ) + if err != windows.ERROR_SUCCESS { + return "", err + } // Get result and cleanup + uri := windows.UTF16PtrToString(_uri) + windows.CoTaskMemFree(unsafe.Pointer(_uri)) + return uri, nil +} + +func (i *ICoreWebView2WebResourceRequest) Release() error { + return i.vtbl.CallRelease(unsafe.Pointer(i)) +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2WebResourceRequestedEventArgs.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2WebResourceRequestedEventArgs.go new file mode 100644 index 000000000..9b093bd2a --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2WebResourceRequestedEventArgs.go @@ -0,0 +1,49 @@ +package edge + +import ( + "golang.org/x/sys/windows" + "unsafe" +) + +type _ICoreWebView2WebResourceRequestedEventArgsVtbl struct { + _IUnknownVtbl + GetRequest ComProc + GetResponse ComProc + PutResponse ComProc + GetDeferral ComProc + GetResourceContext ComProc +} + +type ICoreWebView2WebResourceRequestedEventArgs struct { + vtbl *_ICoreWebView2WebResourceRequestedEventArgsVtbl +} + +func (i *ICoreWebView2WebResourceRequestedEventArgs) AddRef() uintptr { + return i.AddRef() +} + +func (i *ICoreWebView2WebResourceRequestedEventArgs) PutResponse(response *ICoreWebView2WebResourceResponse) error { + var err error + + _, _, err = i.vtbl.PutResponse.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(response)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebView2WebResourceRequestedEventArgs) GetRequest() (*ICoreWebView2WebResourceRequest, error) { + var err error + var request *ICoreWebView2WebResourceRequest + _, _, err = i.vtbl.GetRequest.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&request)), + ) + if err != windows.ERROR_SUCCESS { + return nil, err + } + return request, nil +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2WebResourceRequestedEventHandler.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2WebResourceRequestedEventHandler.go new file mode 100644 index 000000000..8cdf58e88 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2WebResourceRequestedEventHandler.go @@ -0,0 +1,48 @@ +package edge + +type _ICoreWebView2WebResourceRequestedEventHandlerVtbl struct { + _IUnknownVtbl + Invoke ComProc +} + +type iCoreWebView2WebResourceRequestedEventHandler struct { + vtbl *_ICoreWebView2WebResourceRequestedEventHandlerVtbl + impl _ICoreWebView2WebResourceRequestedEventHandlerImpl +} + +func _ICoreWebView2WebResourceRequestedEventHandlerIUnknownQueryInterface(this *iCoreWebView2WebResourceRequestedEventHandler, refiid, object uintptr) uintptr { + return this.impl.QueryInterface(refiid, object) +} + +func _ICoreWebView2WebResourceRequestedEventHandlerIUnknownAddRef(this *iCoreWebView2WebResourceRequestedEventHandler) uintptr { + return this.impl.AddRef() +} + +func _ICoreWebView2WebResourceRequestedEventHandlerIUnknownRelease(this *iCoreWebView2WebResourceRequestedEventHandler) uintptr { + return this.impl.Release() +} + +func _ICoreWebView2WebResourceRequestedEventHandlerInvoke(this *iCoreWebView2WebResourceRequestedEventHandler, sender *ICoreWebView2, args *ICoreWebView2WebResourceRequestedEventArgs) uintptr { + return this.impl.WebResourceRequested(sender, args) +} + +type _ICoreWebView2WebResourceRequestedEventHandlerImpl interface { + _IUnknownImpl + WebResourceRequested(sender *ICoreWebView2, args *ICoreWebView2WebResourceRequestedEventArgs) uintptr +} + +var _ICoreWebView2WebResourceRequestedEventHandlerFn = _ICoreWebView2WebResourceRequestedEventHandlerVtbl{ + _IUnknownVtbl{ + NewComProc(_ICoreWebView2WebResourceRequestedEventHandlerIUnknownQueryInterface), + NewComProc(_ICoreWebView2WebResourceRequestedEventHandlerIUnknownAddRef), + NewComProc(_ICoreWebView2WebResourceRequestedEventHandlerIUnknownRelease), + }, + NewComProc(_ICoreWebView2WebResourceRequestedEventHandlerInvoke), +} + +func newICoreWebView2WebResourceRequestedEventHandler(impl _ICoreWebView2WebResourceRequestedEventHandlerImpl) *iCoreWebView2WebResourceRequestedEventHandler { + return &iCoreWebView2WebResourceRequestedEventHandler{ + vtbl: &_ICoreWebView2WebResourceRequestedEventHandlerFn, + impl: impl, + } +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2WebResourceResponse.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2WebResourceResponse.go new file mode 100644 index 000000000..a6a0eddaf --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2WebResourceResponse.go @@ -0,0 +1,26 @@ +package edge + +import "unsafe" + +type _ICoreWebView2WebResourceResponseVtbl struct { + _IUnknownVtbl + GetContent ComProc + PutContent ComProc + GetHeaders ComProc + GetStatusCode ComProc + PutStatusCode ComProc + GetReasonPhrase ComProc + PutReasonPhrase ComProc +} + +type ICoreWebView2WebResourceResponse struct { + vtbl *_ICoreWebView2WebResourceResponseVtbl +} + +func (i *ICoreWebView2WebResourceResponse) AddRef() uintptr { + return i.AddRef() +} + +func (i *ICoreWebView2WebResourceResponse) Release() error { + return i.vtbl.CallRelease(unsafe.Pointer(i)) +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2_2.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2_2.go new file mode 100644 index 000000000..c2441ab2d --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2_2.go @@ -0,0 +1,16 @@ +package edge + +type iCoreWebView2_2Vtbl struct { + iCoreWebView2Vtbl + AddWebResourceResponseReceived ComProc + RemoveWebResourceResponseReceived ComProc + NavigateWithWebResourceRequest ComProc + AddDomContentLoaded ComProc + RemoveDomContentLoaded ComProc + GetCookieManager ComProc + GetEnvironment ComProc +} + +type ICoreWebView2_2 struct { + vtbl *iCoreWebView2_2Vtbl +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2_3.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2_3.go new file mode 100644 index 000000000..9802b8c0c --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebView2_3.go @@ -0,0 +1,60 @@ +package edge + +import ( + "unsafe" + + "golang.org/x/sys/windows" +) + +type iCoreWebView2_3Vtbl struct { + iCoreWebView2_2Vtbl + TrySuspend ComProc + Resume ComProc + GetIsSuspended ComProc + SetVirtualHostNameToFolderMapping ComProc + ClearVirtualHostNameToFolderMapping ComProc +} + +type ICoreWebView2_3 struct { + vtbl *iCoreWebView2_3Vtbl +} + +func (i *ICoreWebView2_3) SetVirtualHostNameToFolderMapping(hostName, folderPath string, accessKind COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND) error { + _hostName, err := windows.UTF16PtrFromString(hostName) + if err != nil { + return err + } + + _folderPath, err := windows.UTF16PtrFromString(folderPath) + if err != nil { + return err + } + + _, _, err = i.vtbl.SetVirtualHostNameToFolderMapping.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(_hostName)), + uintptr(unsafe.Pointer(_folderPath)), + uintptr(accessKind), + ) + if err != windows.ERROR_SUCCESS { + return err + } + + return nil +} + +func (i *ICoreWebView2) GetICoreWebView2_3() *ICoreWebView2_3 { + var result *ICoreWebView2_3 + + iidICoreWebView2_3 := NewGUID("{A0D6DF20-3B92-416D-AA0C-437A9C727857}") + _, _, _ = i.vtbl.QueryInterface.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(iidICoreWebView2_3)), + uintptr(unsafe.Pointer(&result))) + + return result +} + +func (e *Chromium) GetICoreWebView2_3() *ICoreWebView2_3 { + return e.webview.GetICoreWebView2_3() +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebViewSettings.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebViewSettings.go new file mode 100644 index 000000000..701601bc0 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/ICoreWebViewSettings.go @@ -0,0 +1,394 @@ +package edge + +import ( + "golang.org/x/sys/windows" + "unsafe" +) + +// ICoreWebviewSettings is the merged settings class + +type _ICoreWebViewSettingsVtbl struct { + _IUnknownVtbl + GetIsScriptEnabled ComProc + PutIsScriptEnabled ComProc + GetIsWebMessageEnabled ComProc + PutIsWebMessageEnabled ComProc + GetAreDefaultScriptDialogsEnabled ComProc + PutAreDefaultScriptDialogsEnabled ComProc + GetIsStatusBarEnabled ComProc + PutIsStatusBarEnabled ComProc + GetAreDevToolsEnabled ComProc + PutAreDevToolsEnabled ComProc + GetAreDefaultContextMenusEnabled ComProc + PutAreDefaultContextMenusEnabled ComProc + GetAreHostObjectsAllowed ComProc + PutAreHostObjectsAllowed ComProc + GetIsZoomControlEnabled ComProc + PutIsZoomControlEnabled ComProc + GetIsBuiltInErrorPageEnabled ComProc + PutIsBuiltInErrorPageEnabled ComProc + GetUserAgent ComProc + PutUserAgent ComProc + GetAreBrowserAcceleratorKeysEnabled ComProc + PutAreBrowserAcceleratorKeysEnabled ComProc + GetIsPasswordAutosaveEnabled ComProc + PutIsPasswordAutosaveEnabled ComProc + GetIsGeneralAutofillEnabled ComProc + PutIsGeneralAutofillEnabled ComProc + GetIsPinchZoomEnabled ComProc + PutIsPinchZoomEnabled ComProc + GetIsSwipeNavigationEnabled ComProc + PutIsSwipeNavigationEnabled ComProc +} + +type ICoreWebViewSettings struct { + vtbl *_ICoreWebViewSettingsVtbl +} + +func (i *ICoreWebViewSettings) AddRef() uintptr { + return i.AddRef() +} + +func (i *ICoreWebViewSettings) GetIsScriptEnabled() (bool, error) { + var err error + var isScriptEnabled bool + _, _, err = i.vtbl.GetIsScriptEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&isScriptEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return isScriptEnabled, nil +} + +func (i *ICoreWebViewSettings) PutIsScriptEnabled(isScriptEnabled bool) error { + var err error + + _, _, err = i.vtbl.PutIsScriptEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(isScriptEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebViewSettings) GetIsWebMessageEnabled() (bool, error) { + var err error + var isWebMessageEnabled bool + _, _, err = i.vtbl.GetIsWebMessageEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&isWebMessageEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return isWebMessageEnabled, nil +} + +func (i *ICoreWebViewSettings) PutIsWebMessageEnabled(isWebMessageEnabled bool) error { + var err error + + _, _, err = i.vtbl.PutIsWebMessageEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(isWebMessageEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebViewSettings) GetAreDefaultScriptDialogsEnabled() (bool, error) { + var err error + var areDefaultScriptDialogsEnabled bool + _, _, err = i.vtbl.GetAreDefaultScriptDialogsEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&areDefaultScriptDialogsEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return areDefaultScriptDialogsEnabled, nil +} + +func (i *ICoreWebViewSettings) PutAreDefaultScriptDialogsEnabled(areDefaultScriptDialogsEnabled bool) error { + var err error + + _, _, err = i.vtbl.PutAreDefaultScriptDialogsEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(areDefaultScriptDialogsEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebViewSettings) GetIsStatusBarEnabled() (bool, error) { + var err error + var isStatusBarEnabled bool + _, _, err = i.vtbl.GetIsStatusBarEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&isStatusBarEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return isStatusBarEnabled, nil +} + +func (i *ICoreWebViewSettings) PutIsStatusBarEnabled(isStatusBarEnabled bool) error { + var err error + + _, _, err = i.vtbl.PutIsStatusBarEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(isStatusBarEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebViewSettings) GetAreDevToolsEnabled() (bool, error) { + var err error + var areDevToolsEnabled bool + _, _, err = i.vtbl.GetAreDevToolsEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&areDevToolsEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return areDevToolsEnabled, nil +} + +func (i *ICoreWebViewSettings) PutAreDevToolsEnabled(areDevToolsEnabled bool) error { + var err error + _, _, err = i.vtbl.PutAreDevToolsEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(areDevToolsEnabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebViewSettings) GetAreDefaultContextMenusEnabled() (bool, error) { + var err error + var enabled bool + _, _, err = i.vtbl.GetAreDefaultContextMenusEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&enabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return enabled, nil +} + +func (i *ICoreWebViewSettings) PutAreDefaultContextMenusEnabled(enabled bool) error { + var err error + _, _, err = i.vtbl.PutAreDefaultContextMenusEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(enabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebViewSettings) GetAreHostObjectsAllowed() (bool, error) { + var err error + var allowed bool + _, _, err = i.vtbl.GetAreHostObjectsAllowed.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&allowed)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return allowed, nil +} + +func (i *ICoreWebViewSettings) PutAreHostObjectsAllowed(allowed bool) error { + var err error + + _, _, err = i.vtbl.PutAreHostObjectsAllowed.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(allowed)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebViewSettings) GetIsZoomControlEnabled() (bool, error) { + var err error + var enabled bool + _, _, err = i.vtbl.GetIsZoomControlEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&enabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return enabled, nil +} + +func (i *ICoreWebViewSettings) PutIsZoomControlEnabled(enabled bool) error { + var err error + + _, _, err = i.vtbl.PutIsZoomControlEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(enabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebViewSettings) GetIsBuiltInErrorPageEnabled() (bool, error) { + var err error + var enabled bool + _, _, err = i.vtbl.GetIsBuiltInErrorPageEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&enabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return enabled, nil +} + +func (i *ICoreWebViewSettings) PutIsBuiltInErrorPageEnabled(enabled bool) error { + var err error + + _, _, err = i.vtbl.PutIsBuiltInErrorPageEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(enabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebViewSettings) GetUserAgent() (string, error) { + var err error + // Create *uint16 to hold result + var _userAgent *uint16 + _, _, err = i.vtbl.GetUserAgent.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(_userAgent)), + ) + if err != windows.ERROR_SUCCESS { + return "", err + } // Get result and cleanup + userAgent := windows.UTF16PtrToString(_userAgent) + windows.CoTaskMemFree(unsafe.Pointer(_userAgent)) + return userAgent, nil +} + +func (i *ICoreWebViewSettings) PutUserAgent(userAgent string) error { + var err error + // Convert string 'userAgent' to *uint16 + _userAgent, err := windows.UTF16PtrFromString(userAgent) + if err != nil { + return err + } + + _, _, err = i.vtbl.PutUserAgent.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(_userAgent)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebViewSettings) GetAreBrowserAcceleratorKeysEnabled() (bool, error) { + var err error + var enabled bool + _, _, err = i.vtbl.GetAreBrowserAcceleratorKeysEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&enabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return enabled, nil +} + +func (i *ICoreWebViewSettings) PutAreBrowserAcceleratorKeysEnabled(enabled bool) error { + var err error + + _, _, err = i.vtbl.PutAreBrowserAcceleratorKeysEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(enabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebViewSettings) GetIsPinchZoomEnabled() (bool, error) { + var err error + var enabled bool + _, _, err = i.vtbl.GetIsPinchZoomEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&enabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return enabled, nil +} + +func (i *ICoreWebViewSettings) PutIsPinchZoomEnabled(enabled bool) error { + var err error + + _, _, err = i.vtbl.PutIsPinchZoomEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(enabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +func (i *ICoreWebViewSettings) GetIsSwipeNavigationEnabled() (bool, error) { + var err error + var enabled bool + _, _, err = i.vtbl.GetIsSwipeNavigationEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&enabled)), + ) + if err != windows.ERROR_SUCCESS { + return false, err + } + return enabled, nil +} + +func (i *ICoreWebViewSettings) PutIsSwipeNavigationEnabled(enabled bool) error { + var err error + + _, _, err = i.vtbl.PutIsSwipeNavigationEnabled.Call( + uintptr(unsafe.Pointer(i)), + uintptr(boolToInt(enabled)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/IStream.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/IStream.go new file mode 100644 index 000000000..75ed3e7bc --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/IStream.go @@ -0,0 +1,15 @@ +package edge + +import "unsafe" + +type _IStreamVtbl struct { + _IUnknownVtbl +} + +type IStream struct { + vtbl *_IStreamVtbl +} + +func (i *IStream) Release() error { + return i.vtbl.CallRelease(unsafe.Pointer(i)) +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/chromium.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/chromium.go new file mode 100644 index 000000000..ef9010923 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/chromium.go @@ -0,0 +1,351 @@ +//go:build windows +// +build windows + +package edge + +import ( + "log" + "os" + "path/filepath" + "sync/atomic" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/internal/w32" + "golang.org/x/sys/windows" +) + +type Chromium struct { + hwnd uintptr + controller *ICoreWebView2Controller + webview *ICoreWebView2 + inited uintptr + envCompleted *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler + controllerCompleted *iCoreWebView2CreateCoreWebView2ControllerCompletedHandler + webMessageReceived *iCoreWebView2WebMessageReceivedEventHandler + permissionRequested *iCoreWebView2PermissionRequestedEventHandler + webResourceRequested *iCoreWebView2WebResourceRequestedEventHandler + acceleratorKeyPressed *ICoreWebView2AcceleratorKeyPressedEventHandler + navigationCompleted *ICoreWebView2NavigationCompletedEventHandler + + environment *ICoreWebView2Environment + + // Settings + Debug bool + DataPath string + + // permissions + permissions map[CoreWebView2PermissionKind]CoreWebView2PermissionState + globalPermission *CoreWebView2PermissionState + + // Callbacks + MessageCallback func(string) + WebResourceRequestedCallback func(request *ICoreWebView2WebResourceRequest, args *ICoreWebView2WebResourceRequestedEventArgs) + NavigationCompletedCallback func(sender *ICoreWebView2, args *ICoreWebView2NavigationCompletedEventArgs) + AcceleratorKeyCallback func(uint) bool +} + +func NewChromium() *Chromium { + e := &Chromium{} + /* + All these handlers are passed to native code through syscalls with 'uintptr(unsafe.Pointer(handler))' and we know + that a pointer to those will be kept in the native code. Furthermore these handlers als contain pointer to other Go + structs like the vtable. + This violates the unsafe.Pointer rule '(4) Conversion of a Pointer to a uintptr when calling syscall.Syscall.' because + theres no guarantee that Go doesn't move these objects. + AFAIK currently the Go runtime doesn't move HEAP objects, so we should be safe with these handlers. But they don't + guarantee it, because in the future Go might use a compacting GC. + There's a proposal to add a runtime.Pin function, to prevent moving pinned objects, which would allow to easily fix + this issue by just pinning the handlers. The https://go-review.googlesource.com/c/go/+/367296/ should land in Go 1.19. + */ + e.envCompleted = newICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler(e) + e.controllerCompleted = newICoreWebView2CreateCoreWebView2ControllerCompletedHandler(e) + e.webMessageReceived = newICoreWebView2WebMessageReceivedEventHandler(e) + e.permissionRequested = newICoreWebView2PermissionRequestedEventHandler(e) + e.webResourceRequested = newICoreWebView2WebResourceRequestedEventHandler(e) + e.acceleratorKeyPressed = newICoreWebView2AcceleratorKeyPressedEventHandler(e) + e.navigationCompleted = newICoreWebView2NavigationCompletedEventHandler(e) + e.permissions = make(map[CoreWebView2PermissionKind]CoreWebView2PermissionState) + + return e +} + +func (e *Chromium) Embed(hwnd uintptr) bool { + e.hwnd = hwnd + + dataPath := e.DataPath + if dataPath == "" { + currentExePath := make([]uint16, windows.MAX_PATH) + _, err := windows.GetModuleFileName(windows.Handle(0), ¤tExePath[0], windows.MAX_PATH) + if err != nil { + // What to do here? + return false + } + currentExeName := filepath.Base(windows.UTF16ToString(currentExePath)) + dataPath = filepath.Join(os.Getenv("AppData"), currentExeName) + } + + res, err := createCoreWebView2EnvironmentWithOptions(nil, windows.StringToUTF16Ptr(dataPath), 0, e.envCompleted) + if err != nil { + log.Printf("Error calling Webview2Loader: %v", err) + return false + } else if res != 0 { + log.Printf("Result: %08x", res) + return false + } + var msg w32.Msg + for { + if atomic.LoadUintptr(&e.inited) != 0 { + break + } + r, _, _ := w32.User32GetMessageW.Call( + uintptr(unsafe.Pointer(&msg)), + 0, + 0, + 0, + ) + if r == 0 { + break + } + w32.User32TranslateMessage.Call(uintptr(unsafe.Pointer(&msg))) + w32.User32DispatchMessageW.Call(uintptr(unsafe.Pointer(&msg))) + } + e.Init("window.external={invoke:s=>window.chrome.webview.postMessage(s)}") + return true +} + +func (e *Chromium) Navigate(url string) { + e.webview.vtbl.Navigate.Call( + uintptr(unsafe.Pointer(e.webview)), + uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(url))), + ) +} + +func (e *Chromium) Init(script string) { + e.webview.vtbl.AddScriptToExecuteOnDocumentCreated.Call( + uintptr(unsafe.Pointer(e.webview)), + uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(script))), + 0, + ) +} + +func (e *Chromium) Eval(script string) { + + _script, err := windows.UTF16PtrFromString(script) + if err != nil { + log.Fatal(err) + } + + e.webview.vtbl.ExecuteScript.Call( + uintptr(unsafe.Pointer(e.webview)), + uintptr(unsafe.Pointer(_script)), + 0, + ) +} + +func (e *Chromium) Show() error { + return e.controller.PutIsVisible(true) +} + +func (e *Chromium) Hide() error { + return e.controller.PutIsVisible(false) +} + +func (e *Chromium) QueryInterface(_, _ uintptr) uintptr { + return 0 +} + +func (e *Chromium) AddRef() uintptr { + return 1 +} + +func (e *Chromium) Release() uintptr { + return 1 +} + +func (e *Chromium) EnvironmentCompleted(res uintptr, env *ICoreWebView2Environment) uintptr { + if int64(res) < 0 { + log.Fatalf("Creating environment failed with %08x", res) + } + env.vtbl.AddRef.Call(uintptr(unsafe.Pointer(env))) + e.environment = env + env.vtbl.CreateCoreWebView2Controller.Call( + uintptr(unsafe.Pointer(env)), + e.hwnd, + uintptr(unsafe.Pointer(e.controllerCompleted)), + ) + return 0 +} + +func (e *Chromium) CreateCoreWebView2ControllerCompleted(res uintptr, controller *ICoreWebView2Controller) uintptr { + if int64(res) < 0 { + log.Fatalf("Creating controller failed with %08x", res) + } + controller.vtbl.AddRef.Call(uintptr(unsafe.Pointer(controller))) + e.controller = controller + + var token _EventRegistrationToken + controller.vtbl.GetCoreWebView2.Call( + uintptr(unsafe.Pointer(controller)), + uintptr(unsafe.Pointer(&e.webview)), + ) + e.webview.vtbl.AddRef.Call( + uintptr(unsafe.Pointer(e.webview)), + ) + e.webview.vtbl.AddWebMessageReceived.Call( + uintptr(unsafe.Pointer(e.webview)), + uintptr(unsafe.Pointer(e.webMessageReceived)), + uintptr(unsafe.Pointer(&token)), + ) + e.webview.vtbl.AddPermissionRequested.Call( + uintptr(unsafe.Pointer(e.webview)), + uintptr(unsafe.Pointer(e.permissionRequested)), + uintptr(unsafe.Pointer(&token)), + ) + e.webview.vtbl.AddWebResourceRequested.Call( + uintptr(unsafe.Pointer(e.webview)), + uintptr(unsafe.Pointer(e.webResourceRequested)), + uintptr(unsafe.Pointer(&token)), + ) + e.webview.vtbl.AddNavigationCompleted.Call( + uintptr(unsafe.Pointer(e.webview)), + uintptr(unsafe.Pointer(e.navigationCompleted)), + uintptr(unsafe.Pointer(&token)), + ) + + e.controller.AddAcceleratorKeyPressed(e.acceleratorKeyPressed, &token) + + atomic.StoreUintptr(&e.inited, 1) + + return 0 +} + +func (e *Chromium) MessageReceived(sender *ICoreWebView2, args *iCoreWebView2WebMessageReceivedEventArgs) uintptr { + var message *uint16 + args.vtbl.TryGetWebMessageAsString.Call( + uintptr(unsafe.Pointer(args)), + uintptr(unsafe.Pointer(&message)), + ) + if e.MessageCallback != nil { + e.MessageCallback(w32.Utf16PtrToString(message)) + } + sender.vtbl.PostWebMessageAsString.Call( + uintptr(unsafe.Pointer(sender)), + uintptr(unsafe.Pointer(message)), + ) + windows.CoTaskMemFree(unsafe.Pointer(message)) + return 0 +} + +func (e *Chromium) SetPermission(kind CoreWebView2PermissionKind, state CoreWebView2PermissionState) { + e.permissions[kind] = state +} + +func (e *Chromium) SetGlobalPermission(state CoreWebView2PermissionState) { + e.globalPermission = &state +} + +func (e *Chromium) PermissionRequested(_ *ICoreWebView2, args *iCoreWebView2PermissionRequestedEventArgs) uintptr { + var kind CoreWebView2PermissionKind + args.vtbl.GetPermissionKind.Call( + uintptr(unsafe.Pointer(args)), + uintptr(kind), + ) + var result CoreWebView2PermissionState + if e.globalPermission != nil { + result = *e.globalPermission + } else { + var ok bool + result, ok = e.permissions[kind] + if !ok { + result = CoreWebView2PermissionStateDefault + } + } + args.vtbl.PutState.Call( + uintptr(unsafe.Pointer(args)), + uintptr(result), + ) + return 0 +} + +func (e *Chromium) WebResourceRequested(sender *ICoreWebView2, args *ICoreWebView2WebResourceRequestedEventArgs) uintptr { + req, err := args.GetRequest() + if err != nil { + log.Fatal(err) + } + defer req.Release() + + if e.WebResourceRequestedCallback != nil { + e.WebResourceRequestedCallback(req, args) + } + return 0 +} + +func (e *Chromium) AddWebResourceRequestedFilter(filter string, ctx COREWEBVIEW2_WEB_RESOURCE_CONTEXT) { + err := e.webview.AddWebResourceRequestedFilter(filter, ctx) + if err != nil { + log.Fatal(err) + } +} + +func (e *Chromium) Environment() *ICoreWebView2Environment { + return e.environment +} + +// AcceleratorKeyPressed is called when an accelerator key is pressed. +// If the AcceleratorKeyCallback method has been set, it will defer handling of the keypress +// to the callback. That callback returns a bool indicating if the event was handled. +func (e *Chromium) AcceleratorKeyPressed(sender *ICoreWebView2Controller, args *ICoreWebView2AcceleratorKeyPressedEventArgs) uintptr { + if e.AcceleratorKeyCallback == nil { + return 0 + } + eventKind, _ := args.GetKeyEventKind() + if eventKind == COREWEBVIEW2_KEY_EVENT_KIND_KEY_DOWN || + eventKind == COREWEBVIEW2_KEY_EVENT_KIND_SYSTEM_KEY_DOWN { + virtualKey, _ := args.GetVirtualKey() + status, _ := args.GetPhysicalKeyStatus() + if !status.WasKeyDown { + args.PutHandled(e.AcceleratorKeyCallback(virtualKey)) + return 0 + } + } + args.PutHandled(false) + return 0 +} + +func (e *Chromium) GetSettings() (*ICoreWebViewSettings, error) { + return e.webview.GetSettings() +} + +func (e *Chromium) GetController() *ICoreWebView2Controller { + return e.controller +} + +func boolToInt(input bool) int { + if input { + return 1 + } + return 0 +} + +func (e *Chromium) NavigationCompleted(sender *ICoreWebView2, args *ICoreWebView2NavigationCompletedEventArgs) uintptr { + if e.NavigationCompletedCallback != nil { + e.NavigationCompletedCallback(sender, args) + } + return 0 +} + +func (e *Chromium) NotifyParentWindowPositionChanged() error { + //It looks like the wndproc function is called before the controller initialization is complete. + //Because of this the controller is nil + if e.controller == nil { + return nil + } + return e.controller.NotifyParentWindowPositionChanged() +} + +func (e *Chromium) Focus() { + err := e.controller.MoveFocus(COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC) + if err != nil { + log.Fatal(err) + } +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/chromium_386.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/chromium_386.go new file mode 100644 index 000000000..8362f4bbd --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/chromium_386.go @@ -0,0 +1,24 @@ +//go:build windows +// +build windows + +package edge + +import ( + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/internal/w32" + "unsafe" +) + +func (e *Chromium) Resize() { + if e.controller == nil { + return + } + var bounds w32.Rect + w32.User32GetClientRect.Call(e.hwnd, uintptr(unsafe.Pointer(&bounds))) + e.controller.vtbl.PutBounds.Call( + uintptr(unsafe.Pointer(e.controller)), + uintptr(bounds.Left), + uintptr(bounds.Top), + uintptr(bounds.Right), + uintptr(bounds.Bottom), + ) +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/chromium_amd64.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/chromium_amd64.go new file mode 100644 index 000000000..9835e3ecb --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/chromium_amd64.go @@ -0,0 +1,21 @@ +//go:build windows +// +build windows + +package edge + +import ( + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/internal/w32" + "unsafe" +) + +func (e *Chromium) Resize() { + if e.controller == nil { + return + } + var bounds w32.Rect + w32.User32GetClientRect.Call(e.hwnd, uintptr(unsafe.Pointer(&bounds))) + e.controller.vtbl.PutBounds.Call( + uintptr(unsafe.Pointer(e.controller)), + uintptr(unsafe.Pointer(&bounds)), + ) +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/chromium_arm64.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/chromium_arm64.go new file mode 100644 index 000000000..92b2e2978 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/chromium_arm64.go @@ -0,0 +1,26 @@ +//go:build windows +// +build windows + +package edge + +import ( + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/internal/w32" +) + +func (e *Chromium) Resize() { + if e.controller == nil { + return + } + + var bounds w32.Rect + w32.User32GetClientRect.Call(e.hwnd, uintptr(unsafe.Pointer(&bounds))) + + words := (*[2]uintptr)(unsafe.Pointer(&bounds)) + e.controller.vtbl.PutBounds.Call( + uintptr(unsafe.Pointer(e.controller)), + words[0], + words[1], + ) +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/corewebview2.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/corewebview2.go new file mode 100644 index 000000000..9c23da2f3 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/corewebview2.go @@ -0,0 +1,480 @@ +//go:build windows +// +build windows + +package edge + +import ( + "log" + "runtime" + "syscall" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/internal/w32" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/webviewloader" + "golang.org/x/sys/windows" +) + +func init() { + runtime.LockOSThread() + + r, _, _ := w32.Ole32CoInitializeEx.Call(0, 2) + if int(r) < 0 { + log.Printf("Warning: CoInitializeEx call failed: E=%08x", r) + } +} + +type _EventRegistrationToken struct { + value int64 +} + +type CoreWebView2PermissionKind uint32 + +const ( + CoreWebView2PermissionKindUnknownPermission CoreWebView2PermissionKind = iota + CoreWebView2PermissionKindMicrophone + CoreWebView2PermissionKindCamera + CoreWebView2PermissionKindGeolocation + CoreWebView2PermissionKindNotifications + CoreWebView2PermissionKindOtherSensors + CoreWebView2PermissionKindClipboardRead +) + +type CoreWebView2PermissionState uint32 + +const ( + CoreWebView2PermissionStateDefault CoreWebView2PermissionState = iota + CoreWebView2PermissionStateAllow + CoreWebView2PermissionStateDeny +) + +func createCoreWebView2EnvironmentWithOptions(browserExecutableFolder, userDataFolder *uint16, environmentOptions uintptr, environmentCompletedHandle *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler) (uintptr, error) { + return webviewloader.CreateCoreWebView2EnvironmentWithOptions( + browserExecutableFolder, + userDataFolder, + environmentOptions, + uintptr(unsafe.Pointer(environmentCompletedHandle)), + ) +} + +// ComProc stores a COM procedure. +type ComProc uintptr + +// NewComProc creates a new COM proc from a Go function. +func NewComProc(fn interface{}) ComProc { + return ComProc(windows.NewCallback(fn)) +} + +//go:uintptrescapes +// Call calls a COM procedure. +func (p ComProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) { + // The magic uintptrescapes comment is needed to prevent moving uintptr(unsafe.Pointer(p)) so calls to .Call() also + // satisfy the unsafe.Pointer rule "(4) Conversion of a Pointer to a uintptr when calling syscall.Syscall." + // Otherwise it might be that pointers get moved, especially pointer onto the Go stack which might grow dynamically. + // See https://pkg.go.dev/unsafe#Pointer and https://github.com/golang/go/issues/34474 + switch len(a) { + case 0: + return syscall.Syscall(uintptr(p), 0, 0, 0, 0) + case 1: + return syscall.Syscall(uintptr(p), 1, a[0], 0, 0) + case 2: + return syscall.Syscall(uintptr(p), 2, a[0], a[1], 0) + case 3: + return syscall.Syscall(uintptr(p), 3, a[0], a[1], a[2]) + case 4: + return syscall.Syscall6(uintptr(p), 4, a[0], a[1], a[2], a[3], 0, 0) + case 5: + return syscall.Syscall6(uintptr(p), 5, a[0], a[1], a[2], a[3], a[4], 0) + case 6: + return syscall.Syscall6(uintptr(p), 6, a[0], a[1], a[2], a[3], a[4], a[5]) + case 7: + return syscall.Syscall9(uintptr(p), 7, a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0) + case 8: + return syscall.Syscall9(uintptr(p), 8, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0) + case 9: + return syscall.Syscall9(uintptr(p), 9, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]) + case 10: + return syscall.Syscall12(uintptr(p), 10, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], 0, 0) + case 11: + return syscall.Syscall12(uintptr(p), 11, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], 0) + case 12: + return syscall.Syscall12(uintptr(p), 12, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11]) + case 13: + return syscall.Syscall15(uintptr(p), 13, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], 0, 0) + case 14: + return syscall.Syscall15(uintptr(p), 14, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], 0) + case 15: + return syscall.Syscall15(uintptr(p), 15, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14]) + default: + panic("too many arguments") + } +} + +// IUnknown + +type _IUnknownVtbl struct { + QueryInterface ComProc + AddRef ComProc + Release ComProc +} + +func (i *_IUnknownVtbl) CallRelease(this unsafe.Pointer) error { + _, _, err := i.Release.Call( + uintptr(this), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} + +type _IUnknownImpl interface { + QueryInterface(refiid, object uintptr) uintptr + AddRef() uintptr + Release() uintptr +} + +// ICoreWebView2 + +type iCoreWebView2Vtbl struct { + _IUnknownVtbl + GetSettings ComProc + GetSource ComProc + Navigate ComProc + NavigateToString ComProc + AddNavigationStarting ComProc + RemoveNavigationStarting ComProc + AddContentLoading ComProc + RemoveContentLoading ComProc + AddSourceChanged ComProc + RemoveSourceChanged ComProc + AddHistoryChanged ComProc + RemoveHistoryChanged ComProc + AddNavigationCompleted ComProc + RemoveNavigationCompleted ComProc + AddFrameNavigationStarting ComProc + RemoveFrameNavigationStarting ComProc + AddFrameNavigationCompleted ComProc + RemoveFrameNavigationCompleted ComProc + AddScriptDialogOpening ComProc + RemoveScriptDialogOpening ComProc + AddPermissionRequested ComProc + RemovePermissionRequested ComProc + AddProcessFailed ComProc + RemoveProcessFailed ComProc + AddScriptToExecuteOnDocumentCreated ComProc + RemoveScriptToExecuteOnDocumentCreated ComProc + ExecuteScript ComProc + CapturePreview ComProc + Reload ComProc + PostWebMessageAsJSON ComProc + PostWebMessageAsString ComProc + AddWebMessageReceived ComProc + RemoveWebMessageReceived ComProc + CallDevToolsProtocolMethod ComProc + GetBrowserProcessID ComProc + GetCanGoBack ComProc + GetCanGoForward ComProc + GoBack ComProc + GoForward ComProc + GetDevToolsProtocolEventReceiver ComProc + Stop ComProc + AddNewWindowRequested ComProc + RemoveNewWindowRequested ComProc + AddDocumentTitleChanged ComProc + RemoveDocumentTitleChanged ComProc + GetDocumentTitle ComProc + AddHostObjectToScript ComProc + RemoveHostObjectFromScript ComProc + OpenDevToolsWindow ComProc + AddContainsFullScreenElementChanged ComProc + RemoveContainsFullScreenElementChanged ComProc + GetContainsFullScreenElement ComProc + AddWebResourceRequested ComProc + RemoveWebResourceRequested ComProc + AddWebResourceRequestedFilter ComProc + RemoveWebResourceRequestedFilter ComProc + AddWindowCloseRequested ComProc + RemoveWindowCloseRequested ComProc +} + +type ICoreWebView2 struct { + vtbl *iCoreWebView2Vtbl +} + +func (i *ICoreWebView2) GetSettings() (*ICoreWebViewSettings, error) { + var err error + var settings *ICoreWebViewSettings + _, _, err = i.vtbl.GetSettings.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(&settings)), + ) + if err != windows.ERROR_SUCCESS { + return nil, err + } + return settings, nil +} + +// ICoreWebView2Environment + +type iCoreWebView2EnvironmentVtbl struct { + _IUnknownVtbl + CreateCoreWebView2Controller ComProc + CreateWebResourceResponse ComProc + GetBrowserVersionString ComProc + AddNewBrowserVersionAvailable ComProc + RemoveNewBrowserVersionAvailable ComProc +} + +type ICoreWebView2Environment struct { + vtbl *iCoreWebView2EnvironmentVtbl +} + +// CreateWebResourceResponse creates a new ICoreWebView2WebResourceResponse, it must be released after finishing using it. +func (e *ICoreWebView2Environment) CreateWebResourceResponse(content []byte, statusCode int, reasonPhrase string, headers string) (*ICoreWebView2WebResourceResponse, error) { + var err error + var stream uintptr + + if len(content) > 0 { + // Create stream for response + stream, err = w32.SHCreateMemStream(content) + if err != nil { + return nil, err + } + + // Release the IStream after we are finished, CreateWebResourceResponse Call will increase the reference + // count on IStream and therefore it won't be freed until the reference count of the response is 0. + defer (*IStream)(unsafe.Pointer(stream)).Release() + } + + // Convert string 'uri' to *uint16 + _reason, err := windows.UTF16PtrFromString(reasonPhrase) + if err != nil { + return nil, err + } + // Convert string 'uri' to *uint16 + _headers, err := windows.UTF16PtrFromString(headers) + if err != nil { + return nil, err + } + var response *ICoreWebView2WebResourceResponse + _, _, err = e.vtbl.CreateWebResourceResponse.Call( + uintptr(unsafe.Pointer(e)), + stream, + uintptr(statusCode), + uintptr(unsafe.Pointer(_reason)), + uintptr(unsafe.Pointer(_headers)), + uintptr(unsafe.Pointer(&response)), + ) + if err != windows.ERROR_SUCCESS { + return nil, err + } + return response, nil + +} + +// ICoreWebView2WebMessageReceivedEventArgs + +type iCoreWebView2WebMessageReceivedEventArgsVtbl struct { + _IUnknownVtbl + GetSource ComProc + GetWebMessageAsJSON ComProc + TryGetWebMessageAsString ComProc +} + +type iCoreWebView2WebMessageReceivedEventArgs struct { + vtbl *iCoreWebView2WebMessageReceivedEventArgsVtbl +} + +// ICoreWebView2PermissionRequestedEventArgs + +type iCoreWebView2PermissionRequestedEventArgsVtbl struct { + _IUnknownVtbl + GetURI ComProc + GetPermissionKind ComProc + GetIsUserInitiated ComProc + GetState ComProc + PutState ComProc + GetDeferral ComProc +} + +type iCoreWebView2PermissionRequestedEventArgs struct { + vtbl *iCoreWebView2PermissionRequestedEventArgsVtbl +} + +// ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler + +type iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerImpl interface { + _IUnknownImpl + EnvironmentCompleted(res uintptr, env *ICoreWebView2Environment) uintptr +} + +type iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl struct { + _IUnknownVtbl + Invoke ComProc +} + +type iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler struct { + vtbl *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl + impl iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerImpl +} + +func _ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerIUnknownQueryInterface(this *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler, refiid, object uintptr) uintptr { + return this.impl.QueryInterface(refiid, object) +} + +func _ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerIUnknownAddRef(this *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler) uintptr { + return this.impl.AddRef() +} + +func _ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerIUnknownRelease(this *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler) uintptr { + return this.impl.Release() +} + +func _ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerInvoke(this *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler, res uintptr, env *ICoreWebView2Environment) uintptr { + return this.impl.EnvironmentCompleted(res, env) +} + +var iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerFn = iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl{ + _IUnknownVtbl{ + NewComProc(_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerIUnknownQueryInterface), + NewComProc(_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerIUnknownAddRef), + NewComProc(_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerIUnknownRelease), + }, + NewComProc(_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerInvoke), +} + +func newICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler(impl iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerImpl) *iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler { + return &iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandler{ + vtbl: &iCoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerFn, + impl: impl, + } +} + +// ICoreWebView2WebMessageReceivedEventHandler + +type iCoreWebView2WebMessageReceivedEventHandlerImpl interface { + _IUnknownImpl + MessageReceived(sender *ICoreWebView2, args *iCoreWebView2WebMessageReceivedEventArgs) uintptr +} + +type iCoreWebView2WebMessageReceivedEventHandlerVtbl struct { + _IUnknownVtbl + Invoke ComProc +} + +type iCoreWebView2WebMessageReceivedEventHandler struct { + vtbl *iCoreWebView2WebMessageReceivedEventHandlerVtbl + impl iCoreWebView2WebMessageReceivedEventHandlerImpl +} + +func _ICoreWebView2WebMessageReceivedEventHandlerIUnknownQueryInterface(this *iCoreWebView2WebMessageReceivedEventHandler, refiid, object uintptr) uintptr { + return this.impl.QueryInterface(refiid, object) +} + +func _ICoreWebView2WebMessageReceivedEventHandlerIUnknownAddRef(this *iCoreWebView2WebMessageReceivedEventHandler) uintptr { + return this.impl.AddRef() +} + +func _ICoreWebView2WebMessageReceivedEventHandlerIUnknownRelease(this *iCoreWebView2WebMessageReceivedEventHandler) uintptr { + return this.impl.Release() +} + +func _ICoreWebView2WebMessageReceivedEventHandlerInvoke(this *iCoreWebView2WebMessageReceivedEventHandler, sender *ICoreWebView2, args *iCoreWebView2WebMessageReceivedEventArgs) uintptr { + return this.impl.MessageReceived(sender, args) +} + +var iCoreWebView2WebMessageReceivedEventHandlerFn = iCoreWebView2WebMessageReceivedEventHandlerVtbl{ + _IUnknownVtbl{ + NewComProc(_ICoreWebView2WebMessageReceivedEventHandlerIUnknownQueryInterface), + NewComProc(_ICoreWebView2WebMessageReceivedEventHandlerIUnknownAddRef), + NewComProc(_ICoreWebView2WebMessageReceivedEventHandlerIUnknownRelease), + }, + NewComProc(_ICoreWebView2WebMessageReceivedEventHandlerInvoke), +} + +func newICoreWebView2WebMessageReceivedEventHandler(impl iCoreWebView2WebMessageReceivedEventHandlerImpl) *iCoreWebView2WebMessageReceivedEventHandler { + return &iCoreWebView2WebMessageReceivedEventHandler{ + vtbl: &iCoreWebView2WebMessageReceivedEventHandlerFn, + impl: impl, + } +} + +// ICoreWebView2PermissionRequestedEventHandler + +type iCoreWebView2PermissionRequestedEventHandlerImpl interface { + _IUnknownImpl + PermissionRequested(sender *ICoreWebView2, args *iCoreWebView2PermissionRequestedEventArgs) uintptr +} + +type iCoreWebView2PermissionRequestedEventHandlerVtbl struct { + _IUnknownVtbl + Invoke ComProc +} + +type iCoreWebView2PermissionRequestedEventHandler struct { + vtbl *iCoreWebView2PermissionRequestedEventHandlerVtbl + impl iCoreWebView2PermissionRequestedEventHandlerImpl +} + +func _ICoreWebView2PermissionRequestedEventHandlerIUnknownQueryInterface(this *iCoreWebView2PermissionRequestedEventHandler, refiid, object uintptr) uintptr { + return this.impl.QueryInterface(refiid, object) +} + +func _ICoreWebView2PermissionRequestedEventHandlerIUnknownAddRef(this *iCoreWebView2PermissionRequestedEventHandler) uintptr { + return this.impl.AddRef() +} + +func _ICoreWebView2PermissionRequestedEventHandlerIUnknownRelease(this *iCoreWebView2PermissionRequestedEventHandler) uintptr { + return this.impl.Release() +} + +func _ICoreWebView2PermissionRequestedEventHandlerInvoke(this *iCoreWebView2PermissionRequestedEventHandler, sender *ICoreWebView2, args *iCoreWebView2PermissionRequestedEventArgs) uintptr { + return this.impl.PermissionRequested(sender, args) +} + +var iCoreWebView2PermissionRequestedEventHandlerFn = iCoreWebView2PermissionRequestedEventHandlerVtbl{ + _IUnknownVtbl{ + NewComProc(_ICoreWebView2PermissionRequestedEventHandlerIUnknownQueryInterface), + NewComProc(_ICoreWebView2PermissionRequestedEventHandlerIUnknownAddRef), + NewComProc(_ICoreWebView2PermissionRequestedEventHandlerIUnknownRelease), + }, + NewComProc(_ICoreWebView2PermissionRequestedEventHandlerInvoke), +} + +func newICoreWebView2PermissionRequestedEventHandler(impl iCoreWebView2PermissionRequestedEventHandlerImpl) *iCoreWebView2PermissionRequestedEventHandler { + return &iCoreWebView2PermissionRequestedEventHandler{ + vtbl: &iCoreWebView2PermissionRequestedEventHandlerFn, + impl: impl, + } +} + +func (i *ICoreWebView2) AddWebResourceRequestedFilter(uri string, resourceContext COREWEBVIEW2_WEB_RESOURCE_CONTEXT) error { + var err error + // Convert string 'uri' to *uint16 + _uri, err := windows.UTF16PtrFromString(uri) + if err != nil { + return err + } + _, _, err = i.vtbl.AddWebResourceRequestedFilter.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(_uri)), + uintptr(resourceContext), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} +func (i *ICoreWebView2) AddNavigationCompleted(eventHandler *ICoreWebView2NavigationCompletedEventHandler, token *_EventRegistrationToken) error { + var err error + _, _, err = i.vtbl.AddNavigationCompleted.Call( + uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(eventHandler)), + uintptr(unsafe.Pointer(&token)), + ) + if err != windows.ERROR_SUCCESS { + return err + } + return nil +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/guid.go b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/guid.go new file mode 100644 index 000000000..64d89534f --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge/guid.go @@ -0,0 +1,223 @@ +package edge + +// This code has been adapted from: https://github.com/go-ole/go-ole + +/* + +The MIT License (MIT) + +Copyright © 2013-2017 Yasuhiro Matsumoto, + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +const hextable = "0123456789ABCDEF" +const emptyGUID = "{00000000-0000-0000-0000-000000000000}" + +// GUID is Windows API specific GUID type. +// +// This exists to match Windows GUID type for direct passing for COM. +// Format is in xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx. +type GUID struct { + Data1 uint32 + Data2 uint16 + Data3 uint16 + Data4 [8]byte +} + +// NewGUID converts the given string into a globally unique identifier that is +// compliant with the Windows API. +// +// The supplied string may be in any of these formats: +// +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX +// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} +// +// The conversion of the supplied string is not case-sensitive. +func NewGUID(guid string) *GUID { + d := []byte(guid) + var d1, d2, d3, d4a, d4b []byte + + switch len(d) { + case 38: + if d[0] != '{' || d[37] != '}' { + return nil + } + d = d[1:37] + fallthrough + case 36: + if d[8] != '-' || d[13] != '-' || d[18] != '-' || d[23] != '-' { + return nil + } + d1 = d[0:8] + d2 = d[9:13] + d3 = d[14:18] + d4a = d[19:23] + d4b = d[24:36] + case 32: + d1 = d[0:8] + d2 = d[8:12] + d3 = d[12:16] + d4a = d[16:20] + d4b = d[20:32] + default: + return nil + } + + var g GUID + var ok1, ok2, ok3, ok4 bool + g.Data1, ok1 = decodeHexUint32(d1) + g.Data2, ok2 = decodeHexUint16(d2) + g.Data3, ok3 = decodeHexUint16(d3) + g.Data4, ok4 = decodeHexByte64(d4a, d4b) + if ok1 && ok2 && ok3 && ok4 { + return &g + } + return nil +} + +func decodeHexUint32(src []byte) (value uint32, ok bool) { + var b1, b2, b3, b4 byte + var ok1, ok2, ok3, ok4 bool + b1, ok1 = decodeHexByte(src[0], src[1]) + b2, ok2 = decodeHexByte(src[2], src[3]) + b3, ok3 = decodeHexByte(src[4], src[5]) + b4, ok4 = decodeHexByte(src[6], src[7]) + value = (uint32(b1) << 24) | (uint32(b2) << 16) | (uint32(b3) << 8) | uint32(b4) + ok = ok1 && ok2 && ok3 && ok4 + return +} + +func decodeHexUint16(src []byte) (value uint16, ok bool) { + var b1, b2 byte + var ok1, ok2 bool + b1, ok1 = decodeHexByte(src[0], src[1]) + b2, ok2 = decodeHexByte(src[2], src[3]) + value = (uint16(b1) << 8) | uint16(b2) + ok = ok1 && ok2 + return +} + +func decodeHexByte64(s1 []byte, s2 []byte) (value [8]byte, ok bool) { + var ok1, ok2, ok3, ok4, ok5, ok6, ok7, ok8 bool + value[0], ok1 = decodeHexByte(s1[0], s1[1]) + value[1], ok2 = decodeHexByte(s1[2], s1[3]) + value[2], ok3 = decodeHexByte(s2[0], s2[1]) + value[3], ok4 = decodeHexByte(s2[2], s2[3]) + value[4], ok5 = decodeHexByte(s2[4], s2[5]) + value[5], ok6 = decodeHexByte(s2[6], s2[7]) + value[6], ok7 = decodeHexByte(s2[8], s2[9]) + value[7], ok8 = decodeHexByte(s2[10], s2[11]) + ok = ok1 && ok2 && ok3 && ok4 && ok5 && ok6 && ok7 && ok8 + return +} + +func decodeHexByte(c1, c2 byte) (value byte, ok bool) { + var n1, n2 byte + var ok1, ok2 bool + n1, ok1 = decodeHexChar(c1) + n2, ok2 = decodeHexChar(c2) + value = (n1 << 4) | n2 + ok = ok1 && ok2 + return +} + +func decodeHexChar(c byte) (byte, bool) { + switch { + case '0' <= c && c <= '9': + return c - '0', true + case 'a' <= c && c <= 'f': + return c - 'a' + 10, true + case 'A' <= c && c <= 'F': + return c - 'A' + 10, true + } + + return 0, false +} + +// String converts the GUID to string form. It will adhere to this pattern: +// +// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} +// +// If the GUID is nil, the string representation of an empty GUID is returned: +// +// {00000000-0000-0000-0000-000000000000} +func (guid *GUID) String() string { + if guid == nil { + return emptyGUID + } + + var c [38]byte + c[0] = '{' + putUint32Hex(c[1:9], guid.Data1) + c[9] = '-' + putUint16Hex(c[10:14], guid.Data2) + c[14] = '-' + putUint16Hex(c[15:19], guid.Data3) + c[19] = '-' + putByteHex(c[20:24], guid.Data4[0:2]) + c[24] = '-' + putByteHex(c[25:37], guid.Data4[2:8]) + c[37] = '}' + return string(c[:]) +} + +func putUint32Hex(b []byte, v uint32) { + b[0] = hextable[byte(v>>24)>>4] + b[1] = hextable[byte(v>>24)&0x0f] + b[2] = hextable[byte(v>>16)>>4] + b[3] = hextable[byte(v>>16)&0x0f] + b[4] = hextable[byte(v>>8)>>4] + b[5] = hextable[byte(v>>8)&0x0f] + b[6] = hextable[byte(v)>>4] + b[7] = hextable[byte(v)&0x0f] +} + +func putUint16Hex(b []byte, v uint16) { + b[0] = hextable[byte(v>>8)>>4] + b[1] = hextable[byte(v>>8)&0x0f] + b[2] = hextable[byte(v)>>4] + b[3] = hextable[byte(v)&0x0f] +} + +func putByteHex(dst, src []byte) { + for i := 0; i < len(src); i++ { + dst[i*2] = hextable[src[i]>>4] + dst[i*2+1] = hextable[src[i]&0x0f] + } +} + +// IsEqualGUID compares two GUID. +// +// Not constant time comparison. +func IsEqualGUID(guid1 *GUID, guid2 *GUID) bool { + return guid1.Data1 == guid2.Data1 && + guid1.Data2 == guid2.Data2 && + guid1.Data3 == guid2.Data3 && + guid1.Data4[0] == guid2.Data4[0] && + guid1.Data4[1] == guid2.Data4[1] && + guid1.Data4[2] == guid2.Data4[2] && + guid1.Data4[3] == guid2.Data4[3] && + guid1.Data4[4] == guid2.Data4[4] && + guid1.Data4[5] == guid2.Data4[5] && + guid1.Data4[6] == guid2.Data4[6] && + guid1.Data4[7] == guid2.Data4[7] +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/arm64/WebView2Loader.dll b/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/arm64/WebView2Loader.dll new file mode 100644 index 0000000000000000000000000000000000000000..cd1c694b845a66c30fef09ca675ae539107477d4 GIT binary patch literal 124328 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 v2 +func CompareBrowserVersions(v1 string, v2 string) (int, error) { + + _v1, err := windows.UTF16PtrFromString(v1) + if err != nil { + return 0, err + } + _v2, err := windows.UTF16PtrFromString(v2) + if err != nil { + return 0, err + } + + nativeErr := nativeModule.Load() + if nativeErr == nil { + nativeErr = nativeCompareBrowserVersions.Find() + } + var result int + if nativeErr != nil { + err := loadFromMemory(nativeErr) + if err != nil { + return 0, fmt.Errorf("Unable to load WebView2Loader.dll from disk: %v -- or from memory: %w", nativeErr, memErr) + } + _, _, err = memCompareBrowserVersions.Call( + uint64(uintptr(unsafe.Pointer(_v1))), + uint64(uintptr(unsafe.Pointer(_v2))), + uint64(uintptr(unsafe.Pointer(&result)))) + } else { + _, _, err = nativeCompareBrowserVersions.Call( + uintptr(unsafe.Pointer(_v1)), + uintptr(unsafe.Pointer(_v2)), + uintptr(unsafe.Pointer(&result))) + } + if err != windows.ERROR_SUCCESS { + return result, err + } + return result, nil +} + +// GetInstalledVersion returns the installed version of the webview2 runtime. +// If there is no version installed, a blank string is returned. +func GetInstalledVersion() (string, error) { + nativeErr := nativeModule.Load() + if nativeErr == nil { + nativeErr = nativeGetAvailableCoreWebView2BrowserVersionString.Find() + } + var err error + var result *uint16 + if nativeErr != nil { + err := loadFromMemory(nativeErr) + if err != nil { + return "", fmt.Errorf("Unable to load WebView2Loader.dll from disk: %v -- or from memory: %w", nativeErr, memErr) + } + _, _, err = memGetAvailableCoreWebView2BrowserVersionString.Call( + uint64(uintptr(unsafe.Pointer(nil))), + uint64(uintptr(unsafe.Pointer(&result)))) + } else { + _, _, err = nativeCompareBrowserVersions.Call( + uintptr(unsafe.Pointer(nil)), + uintptr(unsafe.Pointer(&result))) + } + if err != nil { + return "", err + } + version := windows.UTF16PtrToString(result) + windows.CoTaskMemFree(unsafe.Pointer(result)) + return version, nil +} + +// CreateCoreWebView2EnvironmentWithOptions tries to load WebviewLoader2 and +// call the CreateCoreWebView2EnvironmentWithOptions routine. +func CreateCoreWebView2EnvironmentWithOptions(browserExecutableFolder, userDataFolder *uint16, environmentOptions uintptr, environmentCompletedHandle uintptr) (uintptr, error) { + nativeErr := nativeModule.Load() + if nativeErr == nil { + nativeErr = nativeCreate.Find() + } + if nativeErr != nil { + err := loadFromMemory(nativeErr) + if err != nil { + return 0, err + } + res, _, _ := memCreate.Call( + uint64(uintptr(unsafe.Pointer(browserExecutableFolder))), + uint64(uintptr(unsafe.Pointer(userDataFolder))), + uint64(environmentOptions), + uint64(environmentCompletedHandle), + ) + return uintptr(res), nil + } + res, _, _ := nativeCreate.Call( + uintptr(unsafe.Pointer(browserExecutableFolder)), + uintptr(unsafe.Pointer(userDataFolder)), + environmentOptions, + environmentCompletedHandle, + ) + return res, nil +} + +func loadFromMemory(nativeErr error) error { + var err error + // DLL is not available natively. Try loading embedded copy. + memOnce.Do(func() { + memModule, memErr = winloader.LoadFromMemory(WebView2Loader) + if memErr != nil { + err = fmt.Errorf("Unable to load WebView2Loader.dll from disk: %v -- or from memory: %w", nativeErr, memErr) + return + } + memCreate = memModule.Proc("CreateCoreWebView2EnvironmentWithOptions") + memCompareBrowserVersions = memModule.Proc("CompareBrowserVersions") + memGetAvailableCoreWebView2BrowserVersionString = memModule.Proc("GetAvailableCoreWebView2BrowserVersionString") + }) + return err +} diff --git a/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/module_386.go b/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/module_386.go new file mode 100644 index 000000000..174a07037 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/module_386.go @@ -0,0 +1,6 @@ +package webviewloader + +import _ "embed" + +//go:embed x86/WebView2Loader.dll +var WebView2Loader []byte diff --git a/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/module_amd64.go b/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/module_amd64.go new file mode 100644 index 000000000..53e715fdc --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/module_amd64.go @@ -0,0 +1,6 @@ +package webviewloader + +import _ "embed" + +//go:embed x64/WebView2Loader.dll +var WebView2Loader []byte diff --git a/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/module_arm64.go b/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/module_arm64.go new file mode 100644 index 000000000..a09d3fae5 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/module_arm64.go @@ -0,0 +1,6 @@ +package webviewloader + +import _ "embed" + +//go:embed arm64/WebView2Loader.dll +var WebView2Loader []byte diff --git a/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/x64/WebView2Loader.dll b/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/x64/WebView2Loader.dll new file mode 100644 index 0000000000000000000000000000000000000000..ab15cffb45a9d3b243b35dbdda9252cae7dd183e GIT binary patch literal 137640 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.Z?_t#Y)^6H*_u3=Qpjey{kj>u=`zx?AOD>u9Z1a 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%0DC^z2wad3A^i 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 WdhP?WPUTgd^3Mus({knJ(EkT%P(z*o literal 0 HcmV?d00001 diff --git a/v2/internal/frontend/desktop/windows/keys.go b/v2/internal/frontend/desktop/windows/keys.go index bec6eea47..2fe5f7550 100644 --- a/v2/internal/frontend/desktop/windows/keys.go +++ b/v2/internal/frontend/desktop/windows/keys.go @@ -4,7 +4,7 @@ package windows import ( - "github.com/leaanthony/winc" + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc" "github.com/wailsapp/wails/v2/pkg/menu/keys" "strings" ) diff --git a/v2/internal/frontend/desktop/windows/menu.go b/v2/internal/frontend/desktop/windows/menu.go index d1a34fd1e..b71128b45 100644 --- a/v2/internal/frontend/desktop/windows/menu.go +++ b/v2/internal/frontend/desktop/windows/menu.go @@ -4,7 +4,7 @@ package windows import ( - "github.com/leaanthony/winc" + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc" "github.com/wailsapp/wails/v2/pkg/menu" ) diff --git a/v2/internal/frontend/desktop/windows/winc/.gitignore b/v2/internal/frontend/desktop/windows/winc/.gitignore new file mode 100644 index 000000000..f1c181ec9 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/.gitignore @@ -0,0 +1,12 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out diff --git a/v2/internal/frontend/desktop/windows/winc/AUTHORS b/v2/internal/frontend/desktop/windows/winc/AUTHORS new file mode 100644 index 000000000..85674b8d9 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/AUTHORS @@ -0,0 +1,12 @@ +# This is the official list of 'Winc' authors for copyright purposes. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. + +# Contributors +# ============ + +Tad Vizbaras diff --git a/v2/internal/frontend/desktop/windows/winc/LICENSE b/v2/internal/frontend/desktop/windows/winc/LICENSE new file mode 100644 index 000000000..a063a4370 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 winc Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/v2/internal/frontend/desktop/windows/winc/README.md b/v2/internal/frontend/desktop/windows/winc/README.md new file mode 100644 index 000000000..4d4d467bd --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/README.md @@ -0,0 +1,181 @@ +# winc + +** This is a fork of [tadvi/winc](https://github.com/tadvi/winc) for the sole purpose of integration +with [Wails](https://github.com/wailsapp/wails). This repository comes with ***no support*** ** + +Common library for Go GUI apps on Windows. It is for Windows OS only. This makes library smaller than some other UI +libraries for Go. + +Design goals: minimalism and simplicity. + +## Dependencies + +No other dependencies except Go standard library. + +## Building + +If you want to package icon files and other resources into binary **rsrc** tool is recommended: + + rsrc -manifest app.manifest -ico=app.ico,application_edit.ico,application_error.ico -o rsrc.syso + +Here app.manifest is XML file in format: + +``` + + + + + + + + + +``` + +Most Windows applications do not display command prompt. Build your Go project with flag to indicate that it is Windows +GUI binary: + + go build -ldflags="-H windowsgui" + +## Samples + +Best way to learn how to use the library is to look at the included **examples** projects. + +## Setup + +1. Make sure you have a working Go installation and build environment, see more for details on page below. + http://golang.org/doc/install + +2. go get github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc + +## Icons + +When rsrc is used to pack icons into binary it displays IDs of the packed icons. + +``` +rsrc -manifest app.manifest -ico=app.ico,lightning.ico,edit.ico,application_error.ico -o rsrc.syso +Manifest ID: 1 +Icon app.ico ID: 10 +Icon lightning.ico ID: 13 +Icon edit.ico ID: 16 +Icon application_error.ico ID: 19 +``` + +Use IDs to reference packed icons. + +``` +const myIcon = 13 + +btn.SetResIcon(myIcon) // Set icon on the button. +``` + +Included source **examples** use basic building via `release.bat` files. Note that icon IDs are order dependent. So if +you change they order in -ico flag then icon IDs will be different. If you want to keep order the same, just add new +icons to the end of -ico comma separated list. + +## Layout Manager + +SimpleDock is default layout manager. + +Current design of docking and split views allows building simple apps but if you need to have multiple split views in +few different directions you might need to create your own layout manager. + +Important point is to have **one** control inside SimpleDock set to dock as **Fill**. Controls that are not set to any +docking get placed using SetPos() function. So you can have Panel set to dock at the Top and then have another dock to +arrange controls inside that Panel or have controls placed using SetPos() at fixed positions. + +![Example layout with two toolbars and status bar](dock_topbottom.png) + +This is basic layout. Instead of toolbars and status bar you can have Panel or any other control that can resize. Panel +can have its own internal Dock that will arrange other controls inside of it. + +![Example layout with two toolbars and navigation on the left](dock_topleft.png) + +This is layout with extra control(s) on the left. Left side is usually treeview or listview. + +The rule is simple: you either dock controls using SimpleDock OR use SetPos() to set them at fixed positions. That's it. + +At some point **winc** may get more sophisticated layout manager. + +## Dialog Screens + +Dialog screens are not based on Windows resource files (.rc). They are just windows with controls placed at fixed +coordinates. This works fine for dialog screens up to 10-14 controls. + +# Minimal Demo + +``` +package main + +import ( + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc" +) + +func main() { + mainWindow := winc.NewForm(nil) + mainWindow.SetSize(400, 300) // (width, height) + mainWindow.SetText("Hello World Demo") + + edt := winc.NewEdit(mainWindow) + edt.SetPos(10, 20) + // Most Controls have default size unless SetSize is called. + edt.SetText("edit text") + + btn := winc.NewPushButton(mainWindow) + btn.SetText("Show or Hide") + btn.SetPos(40, 50) // (x, y) + btn.SetSize(100, 40) // (width, height) + btn.OnClick().Bind(func(e *winc.Event) { + if edt.Visible() { + edt.Hide() + } else { + edt.Show() + } + }) + + mainWindow.Center() + mainWindow.Show() + mainWindow.OnClose().Bind(wndOnClose) + + winc.RunMainLoop() // Must call to start event loop. +} + +func wndOnClose(arg *winc.Event) { + winc.Exit() +} +``` + +![Hello World](examples/hello.png) + +Result of running sample_minimal. + +## Create Your Own + +It is good practice to create your own controls based on existing structures and event model. Library contains some of +the controls built that way: IconButton (button.go), ErrorPanel (panel.go), MultiEdit (edit.go), etc. Please look at +existing controls as examples before building your own. + +When designing your own controls keep in mind that types have to be converted from Go into Win32 API and back. This is +usually due to string UTF8 and UTF16 conversions. But there are other types of conversions too. + +When developing your own controls you might also need to: + + import "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" + +w32 has Win32 API low level constants and functions. + +Look at **sample_control** for example of custom built window. + +## Companion Package + +[Go package for Windows Systray icon, menu and notifications](https://github.com/tadvi/systray) + +## Credits + +This library is built on + +[AllenDang/gform Windows GUI framework for Go](https://github.com/AllenDang/gform) + +**winc** takes most design decisions from **gform** and adds many more controls and code samples to it. + + diff --git a/v2/internal/frontend/desktop/windows/winc/app.go b/v2/internal/frontend/desktop/windows/winc/app.go new file mode 100644 index 000000000..a2c3237c6 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/app.go @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "runtime" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +var ( + // resource compilation tool assigns app.ico ID of 3 + // rsrc -manifest app.manifest -ico app.ico -o rsrc.syso + AppIconID = 3 +) + +func init() { + runtime.LockOSThread() + + gAppInstance = w32.GetModuleHandle("") + if gAppInstance == 0 { + panic("Error occurred in App.Init") + } + + // Initialize the common controls + var initCtrls w32.INITCOMMONCONTROLSEX + initCtrls.DwSize = uint32(unsafe.Sizeof(initCtrls)) + initCtrls.DwICC = + w32.ICC_LISTVIEW_CLASSES | w32.ICC_PROGRESS_CLASS | w32.ICC_TAB_CLASSES | + w32.ICC_TREEVIEW_CLASSES | w32.ICC_BAR_CLASSES + + w32.InitCommonControlsEx(&initCtrls) +} + +// SetAppIconID sets recource icon ID for the apps windows. +func SetAppIcon(appIconID int) { + AppIconID = appIconID +} + +func GetAppInstance() w32.HINSTANCE { + return gAppInstance +} + +func PreTranslateMessage(msg *w32.MSG) bool { + // This functions is called by the MessageLoop. It processes the + // keyboard accelerator keys and calls Controller.PreTranslateMessage for + // keyboard and mouse events. + + processed := false + + if (msg.Message >= w32.WM_KEYFIRST && msg.Message <= w32.WM_KEYLAST) || + (msg.Message >= w32.WM_MOUSEFIRST && msg.Message <= w32.WM_MOUSELAST) { + + if msg.Hwnd != 0 { + if controller := GetMsgHandler(msg.Hwnd); controller != nil { + // Search the chain of parents for pretranslated messages. + for p := controller; p != nil; p = p.Parent() { + + if processed = p.PreTranslateMessage(msg); processed { + break + } + } + } + } + } + + return processed +} + +// RunMainLoop processes messages in main application loop. +func RunMainLoop() int { + m := (*w32.MSG)(unsafe.Pointer(w32.GlobalAlloc(0, uint32(unsafe.Sizeof(w32.MSG{}))))) + defer w32.GlobalFree(w32.HGLOBAL(unsafe.Pointer(m))) + + for w32.GetMessage(m, 0, 0, 0) != 0 { + + if !PreTranslateMessage(m) { + w32.TranslateMessage(m) + w32.DispatchMessage(m) + } + } + + w32.GdiplusShutdown() + return int(m.WParam) +} + +// PostMessages processes recent messages. Sometimes helpful for instant window refresh. +func PostMessages() { + m := (*w32.MSG)(unsafe.Pointer(w32.GlobalAlloc(0, uint32(unsafe.Sizeof(w32.MSG{}))))) + defer w32.GlobalFree(w32.HGLOBAL(unsafe.Pointer(m))) + + for i := 0; i < 10; i++ { + if w32.GetMessage(m, 0, 0, 0) != 0 { + if !PreTranslateMessage(m) { + w32.TranslateMessage(m) + w32.DispatchMessage(m) + } + } + } +} + +func Exit() { + w32.PostQuitMessage(0) +} diff --git a/v2/internal/frontend/desktop/windows/winc/bitmap.go b/v2/internal/frontend/desktop/windows/winc/bitmap.go new file mode 100644 index 000000000..235dc43fd --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/bitmap.go @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "errors" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type Bitmap struct { + handle w32.HBITMAP + width, height int +} + +func assembleBitmapFromHBITMAP(hbitmap w32.HBITMAP) (*Bitmap, error) { + var dib w32.DIBSECTION + if w32.GetObject(w32.HGDIOBJ(hbitmap), unsafe.Sizeof(dib), unsafe.Pointer(&dib)) == 0 { + return nil, errors.New("GetObject for HBITMAP failed") + } + + return &Bitmap{ + handle: hbitmap, + width: int(dib.DsBmih.BiWidth), + height: int(dib.DsBmih.BiHeight), + }, nil +} + +func NewBitmapFromFile(filepath string, background Color) (*Bitmap, error) { + var gpBitmap *uintptr + var err error + + gpBitmap, err = w32.GdipCreateBitmapFromFile(filepath) + if err != nil { + return nil, err + } + defer w32.GdipDisposeImage(gpBitmap) + + var hbitmap w32.HBITMAP + // Reverse RGB to BGR to satisfy gdiplus color schema. + hbitmap, err = w32.GdipCreateHBITMAPFromBitmap(gpBitmap, uint32(RGB(background.B(), background.G(), background.R()))) + if err != nil { + return nil, err + } + + return assembleBitmapFromHBITMAP(hbitmap) +} + +func NewBitmapFromResource(instance w32.HINSTANCE, resName *uint16, resType *uint16, background Color) (*Bitmap, error) { + var gpBitmap *uintptr + var err error + var hRes w32.HRSRC + + hRes, err = w32.FindResource(w32.HMODULE(instance), resName, resType) + if err != nil { + return nil, err + } + resSize := w32.SizeofResource(w32.HMODULE(instance), hRes) + pResData := w32.LockResource(w32.LoadResource(w32.HMODULE(instance), hRes)) + resBuffer := w32.GlobalAlloc(w32.GMEM_MOVEABLE, resSize) + pResBuffer := w32.GlobalLock(resBuffer) + w32.MoveMemory(pResBuffer, pResData, resSize) + + stream := w32.CreateStreamOnHGlobal(resBuffer, false) + + gpBitmap, err = w32.GdipCreateBitmapFromStream(stream) + if err != nil { + return nil, err + } + defer stream.Release() + defer w32.GlobalUnlock(resBuffer) + defer w32.GlobalFree(resBuffer) + defer w32.GdipDisposeImage(gpBitmap) + + var hbitmap w32.HBITMAP + // Reverse gform.RGB to BGR to satisfy gdiplus color schema. + hbitmap, err = w32.GdipCreateHBITMAPFromBitmap(gpBitmap, uint32(RGB(background.B(), background.G(), background.R()))) + if err != nil { + return nil, err + } + + return assembleBitmapFromHBITMAP(hbitmap) +} + +func (bm *Bitmap) Dispose() { + if bm.handle != 0 { + w32.DeleteObject(w32.HGDIOBJ(bm.handle)) + bm.handle = 0 + } +} + +func (bm *Bitmap) GetHBITMAP() w32.HBITMAP { + return bm.handle +} + +func (bm *Bitmap) Size() (int, int) { + return bm.width, bm.height +} + +func (bm *Bitmap) Height() int { + return bm.height +} + +func (bm *Bitmap) Width() int { + return bm.width +} diff --git a/v2/internal/frontend/desktop/windows/winc/brush.go b/v2/internal/frontend/desktop/windows/winc/brush.go new file mode 100644 index 000000000..55e8ec526 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/brush.go @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +var DefaultBackgroundBrush = NewSystemColorBrush(w32.COLOR_BTNFACE) + +type Brush struct { + hBrush w32.HBRUSH + logBrush w32.LOGBRUSH +} + +func NewSolidColorBrush(color Color) *Brush { + lb := w32.LOGBRUSH{LbStyle: w32.BS_SOLID, LbColor: w32.COLORREF(color)} + hBrush := w32.CreateBrushIndirect(&lb) + if hBrush == 0 { + panic("Faild to create solid color brush") + } + + return &Brush{hBrush, lb} +} + +func NewSystemColorBrush(colorIndex int) *Brush { + //lb := w32.LOGBRUSH{LbStyle: w32.BS_SOLID, LbColor: w32.COLORREF(colorIndex)} + lb := w32.LOGBRUSH{LbStyle: w32.BS_NULL} + hBrush := w32.GetSysColorBrush(colorIndex) + if hBrush == 0 { + panic("GetSysColorBrush failed") + } + return &Brush{hBrush, lb} +} + +func NewHatchedColorBrush(color Color) *Brush { + lb := w32.LOGBRUSH{LbStyle: w32.BS_HATCHED, LbColor: w32.COLORREF(color)} + hBrush := w32.CreateBrushIndirect(&lb) + if hBrush == 0 { + panic("Faild to create solid color brush") + } + + return &Brush{hBrush, lb} +} + +func NewNullBrush() *Brush { + lb := w32.LOGBRUSH{LbStyle: w32.BS_NULL} + hBrush := w32.CreateBrushIndirect(&lb) + if hBrush == 0 { + panic("Failed to create null brush") + } + + return &Brush{hBrush, lb} +} + +func (br *Brush) GetHBRUSH() w32.HBRUSH { + return br.hBrush +} + +func (br *Brush) GetLOGBRUSH() *w32.LOGBRUSH { + return &br.logBrush +} + +func (br *Brush) Dispose() { + if br.hBrush != 0 { + w32.DeleteObject(w32.HGDIOBJ(br.hBrush)) + br.hBrush = 0 + } +} diff --git a/v2/internal/frontend/desktop/windows/winc/buttons.go b/v2/internal/frontend/desktop/windows/winc/buttons.go new file mode 100644 index 000000000..ccf0d267d --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/buttons.go @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "fmt" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type Button struct { + ControlBase + onClick EventManager +} + +func (bt *Button) OnClick() *EventManager { + return &bt.onClick +} + +func (bt *Button) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + switch msg { + case w32.WM_COMMAND: + bt.onClick.Fire(NewEvent(bt, nil)) + /*case w32.WM_LBUTTONDOWN: + w32.SetCapture(bt.Handle()) + case w32.WM_LBUTTONUP: + w32.ReleaseCapture()*/ + /*case win.WM_GETDLGCODE: + println("GETDLGCODE")*/ + } + return w32.DefWindowProc(bt.hwnd, msg, wparam, lparam) + //return bt.W32Control.WndProc(msg, wparam, lparam) +} + +func (bt *Button) Checked() bool { + result := w32.SendMessage(bt.hwnd, w32.BM_GETCHECK, 0, 0) + return result == w32.BST_CHECKED +} + +func (bt *Button) SetChecked(checked bool) { + wparam := w32.BST_CHECKED + if !checked { + wparam = w32.BST_UNCHECKED + } + w32.SendMessage(bt.hwnd, w32.BM_SETCHECK, uintptr(wparam), 0) +} + +// SetIcon sets icon on the button. Recommended icons are 32x32 with 32bit color depth. +func (bt *Button) SetIcon(ico *Icon) { + w32.SendMessage(bt.hwnd, w32.BM_SETIMAGE, w32.IMAGE_ICON, uintptr(ico.handle)) +} + +func (bt *Button) SetResIcon(iconID uint16) { + if ico, err := NewIconFromResource(GetAppInstance(), iconID); err == nil { + bt.SetIcon(ico) + return + } + panic(fmt.Sprintf("missing icon with icon ID: %d", iconID)) +} + +type PushButton struct { + Button +} + +func NewPushButton(parent Controller) *PushButton { + pb := new(PushButton) + + pb.InitControl("BUTTON", parent, 0, w32.BS_PUSHBUTTON|w32.WS_TABSTOP|w32.WS_VISIBLE|w32.WS_CHILD) + RegMsgHandler(pb) + + pb.SetFont(DefaultFont) + pb.SetText("Button") + pb.SetSize(100, 22) + + return pb +} + +// SetDefault is used for dialogs to set default button. +func (pb *PushButton) SetDefault() { + pb.SetAndClearStyleBits(w32.BS_DEFPUSHBUTTON, w32.BS_PUSHBUTTON) +} + +// IconButton does not display text, requires SetResIcon call. +type IconButton struct { + Button +} + +func NewIconButton(parent Controller) *IconButton { + pb := new(IconButton) + + pb.InitControl("BUTTON", parent, 0, w32.BS_ICON|w32.WS_TABSTOP|w32.WS_VISIBLE|w32.WS_CHILD) + RegMsgHandler(pb) + + pb.SetFont(DefaultFont) + // even if text would be set it would not be displayed + pb.SetText("") + pb.SetSize(100, 22) + + return pb +} + +type CheckBox struct { + Button +} + +func NewCheckBox(parent Controller) *CheckBox { + cb := new(CheckBox) + + cb.InitControl("BUTTON", parent, 0, w32.WS_TABSTOP|w32.WS_VISIBLE|w32.WS_CHILD|w32.BS_AUTOCHECKBOX) + RegMsgHandler(cb) + + cb.SetFont(DefaultFont) + cb.SetText("CheckBox") + cb.SetSize(100, 22) + + return cb +} + +type RadioButton struct { + Button +} + +func NewRadioButton(parent Controller) *RadioButton { + rb := new(RadioButton) + + rb.InitControl("BUTTON", parent, 0, w32.WS_TABSTOP|w32.WS_VISIBLE|w32.WS_CHILD|w32.BS_AUTORADIOBUTTON) + RegMsgHandler(rb) + + rb.SetFont(DefaultFont) + rb.SetText("RadioButton") + rb.SetSize(100, 22) + + return rb +} + +type GroupBox struct { + Button +} + +func NewGroupBox(parent Controller) *GroupBox { + gb := new(GroupBox) + + gb.InitControl("BUTTON", parent, 0, w32.WS_CHILD|w32.WS_VISIBLE|w32.WS_GROUP|w32.BS_GROUPBOX) + RegMsgHandler(gb) + + gb.SetFont(DefaultFont) + gb.SetText("GroupBox") + gb.SetSize(100, 100) + + return gb +} diff --git a/v2/internal/frontend/desktop/windows/winc/canvas.go b/v2/internal/frontend/desktop/windows/winc/canvas.go new file mode 100644 index 000000000..46fc0d615 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/canvas.go @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "fmt" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type Canvas struct { + hwnd w32.HWND + hdc w32.HDC + doNotDispose bool +} + +var nullBrush = NewNullBrush() + +func NewCanvasFromHwnd(hwnd w32.HWND) *Canvas { + hdc := w32.GetDC(hwnd) + if hdc == 0 { + panic(fmt.Sprintf("Create canvas from %v failed.", hwnd)) + } + + return &Canvas{hwnd: hwnd, hdc: hdc, doNotDispose: false} +} + +func NewCanvasFromHDC(hdc w32.HDC) *Canvas { + if hdc == 0 { + panic("Cannot create canvas from invalid HDC.") + } + + return &Canvas{hdc: hdc, doNotDispose: true} +} + +func (ca *Canvas) Dispose() { + if !ca.doNotDispose && ca.hdc != 0 { + if ca.hwnd == 0 { + w32.DeleteDC(ca.hdc) + } else { + w32.ReleaseDC(ca.hwnd, ca.hdc) + } + + ca.hdc = 0 + } +} + +func (ca *Canvas) DrawBitmap(bmp *Bitmap, x, y int) { + cdc := w32.CreateCompatibleDC(0) + defer w32.DeleteDC(cdc) + + hbmpOld := w32.SelectObject(cdc, w32.HGDIOBJ(bmp.GetHBITMAP())) + defer w32.SelectObject(cdc, w32.HGDIOBJ(hbmpOld)) + + w, h := bmp.Size() + + w32.BitBlt(ca.hdc, x, y, w, h, cdc, 0, 0, w32.SRCCOPY) +} + +func (ca *Canvas) DrawStretchedBitmap(bmp *Bitmap, rect *Rect) { + cdc := w32.CreateCompatibleDC(0) + defer w32.DeleteDC(cdc) + + hbmpOld := w32.SelectObject(cdc, w32.HGDIOBJ(bmp.GetHBITMAP())) + defer w32.SelectObject(cdc, w32.HGDIOBJ(hbmpOld)) + + w, h := bmp.Size() + + rc := rect.GetW32Rect() + w32.StretchBlt(ca.hdc, int(rc.Left), int(rc.Top), int(rc.Right), int(rc.Bottom), cdc, 0, 0, w, h, w32.SRCCOPY) +} + +func (ca *Canvas) DrawIcon(ico *Icon, x, y int) bool { + return w32.DrawIcon(ca.hdc, x, y, ico.Handle()) +} + +// DrawFillRect draw and fill rectangle with color. +func (ca *Canvas) DrawFillRect(rect *Rect, pen *Pen, brush *Brush) { + w32Rect := rect.GetW32Rect() + + previousPen := w32.SelectObject(ca.hdc, w32.HGDIOBJ(pen.GetHPEN())) + defer w32.SelectObject(ca.hdc, previousPen) + + previousBrush := w32.SelectObject(ca.hdc, w32.HGDIOBJ(brush.GetHBRUSH())) + defer w32.SelectObject(ca.hdc, previousBrush) + + w32.Rectangle(ca.hdc, w32Rect.Left, w32Rect.Top, w32Rect.Right, w32Rect.Bottom) +} + +func (ca *Canvas) DrawRect(rect *Rect, pen *Pen) { + w32Rect := rect.GetW32Rect() + + previousPen := w32.SelectObject(ca.hdc, w32.HGDIOBJ(pen.GetHPEN())) + defer w32.SelectObject(ca.hdc, previousPen) + + // nullBrush is used to make interior of the rect transparent + previousBrush := w32.SelectObject(ca.hdc, w32.HGDIOBJ(nullBrush.GetHBRUSH())) + defer w32.SelectObject(ca.hdc, previousBrush) + + w32.Rectangle(ca.hdc, w32Rect.Left, w32Rect.Top, w32Rect.Right, w32Rect.Bottom) +} + +func (ca *Canvas) FillRect(rect *Rect, brush *Brush) { + w32.FillRect(ca.hdc, rect.GetW32Rect(), brush.GetHBRUSH()) +} + +func (ca *Canvas) DrawEllipse(rect *Rect, pen *Pen) { + w32Rect := rect.GetW32Rect() + + previousPen := w32.SelectObject(ca.hdc, w32.HGDIOBJ(pen.GetHPEN())) + defer w32.SelectObject(ca.hdc, previousPen) + + // nullBrush is used to make interior of the rect transparent + previousBrush := w32.SelectObject(ca.hdc, w32.HGDIOBJ(nullBrush.GetHBRUSH())) + defer w32.SelectObject(ca.hdc, previousBrush) + + w32.Ellipse(ca.hdc, w32Rect.Left, w32Rect.Top, w32Rect.Right, w32Rect.Bottom) +} + +// DrawFillEllipse draw and fill ellipse with color. +func (ca *Canvas) DrawFillEllipse(rect *Rect, pen *Pen, brush *Brush) { + w32Rect := rect.GetW32Rect() + + previousPen := w32.SelectObject(ca.hdc, w32.HGDIOBJ(pen.GetHPEN())) + defer w32.SelectObject(ca.hdc, previousPen) + + previousBrush := w32.SelectObject(ca.hdc, w32.HGDIOBJ(brush.GetHBRUSH())) + defer w32.SelectObject(ca.hdc, previousBrush) + + w32.Ellipse(ca.hdc, w32Rect.Left, w32Rect.Top, w32Rect.Right, w32Rect.Bottom) +} + +func (ca *Canvas) DrawLine(x, y, x2, y2 int, pen *Pen) { + w32.MoveToEx(ca.hdc, x, y, nil) + + previousPen := w32.SelectObject(ca.hdc, w32.HGDIOBJ(pen.GetHPEN())) + defer w32.SelectObject(ca.hdc, previousPen) + + w32.LineTo(ca.hdc, int32(x2), int32(y2)) +} + +// Refer win32 DrawText document for uFormat. +func (ca *Canvas) DrawText(text string, rect *Rect, format uint, font *Font, textColor Color) { + previousFont := w32.SelectObject(ca.hdc, w32.HGDIOBJ(font.GetHFONT())) + defer w32.SelectObject(ca.hdc, w32.HGDIOBJ(previousFont)) + + previousBkMode := w32.SetBkMode(ca.hdc, w32.TRANSPARENT) + defer w32.SetBkMode(ca.hdc, previousBkMode) + + previousTextColor := w32.SetTextColor(ca.hdc, w32.COLORREF(textColor)) + defer w32.SetTextColor(ca.hdc, previousTextColor) + + w32.DrawText(ca.hdc, text, len(text), rect.GetW32Rect(), format) +} diff --git a/v2/internal/frontend/desktop/windows/winc/color.go b/v2/internal/frontend/desktop/windows/winc/color.go new file mode 100644 index 000000000..ac16dbc4b --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/color.go @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +type Color uint32 + +func RGB(r, g, b byte) Color { + return Color(uint32(r) | uint32(g)<<8 | uint32(b)<<16) +} + +func (c Color) R() byte { + return byte(c & 0xff) +} + +func (c Color) G() byte { + return byte((c >> 8) & 0xff) +} + +func (c Color) B() byte { + return byte((c >> 16) & 0xff) +} diff --git a/v2/internal/frontend/desktop/windows/winc/combobox.go b/v2/internal/frontend/desktop/windows/winc/combobox.go new file mode 100644 index 000000000..b389c8b40 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/combobox.go @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + */ + +package winc + +import ( + "syscall" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type ComboBox struct { + ControlBase + onSelectedChange EventManager +} + +func NewComboBox(parent Controller) *ComboBox { + cb := new(ComboBox) + + cb.InitControl("COMBOBOX", parent, 0, w32.WS_CHILD|w32.WS_VISIBLE|w32.WS_TABSTOP|w32.WS_VSCROLL|w32.CBS_DROPDOWNLIST) + RegMsgHandler(cb) + + cb.SetFont(DefaultFont) + cb.SetSize(200, 400) + return cb +} + +func (cb *ComboBox) DeleteAllItems() bool { + return w32.SendMessage(cb.hwnd, w32.CB_RESETCONTENT, 0, 0) == w32.TRUE +} + +func (cb *ComboBox) InsertItem(index int, str string) bool { + lp := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str))) + return w32.SendMessage(cb.hwnd, w32.CB_INSERTSTRING, uintptr(index), lp) != w32.CB_ERR +} + +func (cb *ComboBox) DeleteItem(index int) bool { + return w32.SendMessage(cb.hwnd, w32.CB_DELETESTRING, uintptr(index), 0) != w32.CB_ERR +} + +func (cb *ComboBox) SelectedItem() int { + return int(int32(w32.SendMessage(cb.hwnd, w32.CB_GETCURSEL, 0, 0))) +} + +func (cb *ComboBox) SetSelectedItem(value int) bool { + return int(int32(w32.SendMessage(cb.hwnd, w32.CB_SETCURSEL, uintptr(value), 0))) == value +} + +func (cb *ComboBox) OnSelectedChange() *EventManager { + return &cb.onSelectedChange +} + +// Message processer +func (cb *ComboBox) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + switch msg { + case w32.WM_COMMAND: + code := w32.HIWORD(uint32(wparam)) + + switch code { + case w32.CBN_SELCHANGE: + cb.onSelectedChange.Fire(NewEvent(cb, nil)) + } + } + return w32.DefWindowProc(cb.hwnd, msg, wparam, lparam) + //return cb.W32Control.WndProc(msg, wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/commondlgs.go b/v2/internal/frontend/desktop/windows/winc/commondlgs.go new file mode 100644 index 000000000..3e30f14bc --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/commondlgs.go @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "fmt" + "syscall" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +func genOFN(parent Controller, title, filter string, filterIndex uint, initialDir string, buf []uint16) *w32.OPENFILENAME { + var ofn w32.OPENFILENAME + ofn.StructSize = uint32(unsafe.Sizeof(ofn)) + ofn.Owner = parent.Handle() + + if filter != "" { + filterBuf := make([]uint16, len(filter)+1) + copy(filterBuf, syscall.StringToUTF16(filter)) + // Replace '|' with the expected '\0' + for i, c := range filterBuf { + if byte(c) == '|' { + filterBuf[i] = uint16(0) + } + } + ofn.Filter = &filterBuf[0] + ofn.FilterIndex = uint32(filterIndex) + } + + ofn.File = &buf[0] + ofn.MaxFile = uint32(len(buf)) + + if initialDir != "" { + ofn.InitialDir = syscall.StringToUTF16Ptr(initialDir) + } + if title != "" { + ofn.Title = syscall.StringToUTF16Ptr(title) + } + + ofn.Flags = w32.OFN_FILEMUSTEXIST + return &ofn +} + +func ShowOpenFileDlg(parent Controller, title, filter string, filterIndex uint, initialDir string) (filePath string, accepted bool) { + buf := make([]uint16, 1024) + ofn := genOFN(parent, title, filter, filterIndex, initialDir, buf) + + if accepted = w32.GetOpenFileName(ofn); accepted { + filePath = syscall.UTF16ToString(buf) + } + return +} + +func ShowSaveFileDlg(parent Controller, title, filter string, filterIndex uint, initialDir string) (filePath string, accepted bool) { + buf := make([]uint16, 1024) + ofn := genOFN(parent, title, filter, filterIndex, initialDir, buf) + + if accepted = w32.GetSaveFileName(ofn); accepted { + filePath = syscall.UTF16ToString(buf) + } + return +} + +func ShowBrowseFolderDlg(parent Controller, title string) (folder string, accepted bool) { + var bi w32.BROWSEINFO + bi.Owner = parent.Handle() + bi.Title = syscall.StringToUTF16Ptr(title) + bi.Flags = w32.BIF_RETURNONLYFSDIRS | w32.BIF_NEWDIALOGSTYLE + + w32.CoInitialize() + ret := w32.SHBrowseForFolder(&bi) + w32.CoUninitialize() + + folder = w32.SHGetPathFromIDList(ret) + accepted = folder != "" + return +} + +// MsgBoxOkCancel basic pop up message. Returns 1 for OK and 2 for CANCEL. +func MsgBoxOkCancel(parent Controller, title, caption string) int { + return MsgBox(parent, title, caption, w32.MB_ICONEXCLAMATION|w32.MB_OKCANCEL) +} + +func MsgBoxYesNo(parent Controller, title, caption string) int { + return MsgBox(parent, title, caption, w32.MB_ICONEXCLAMATION|w32.MB_YESNO) +} + +func MsgBoxOk(parent Controller, title, caption string) { + MsgBox(parent, title, caption, w32.MB_ICONINFORMATION|w32.MB_OK) +} + +// Warningf is generic warning message with OK and Cancel buttons. Returns 1 for OK. +func Warningf(parent Controller, format string, data ...interface{}) int { + caption := fmt.Sprintf(format, data...) + return MsgBox(parent, "Warning", caption, w32.MB_ICONWARNING|w32.MB_OKCANCEL) +} + +// Printf is generic info message with OK button. +func Printf(parent Controller, format string, data ...interface{}) { + caption := fmt.Sprintf(format, data...) + MsgBox(parent, "Information", caption, w32.MB_ICONINFORMATION|w32.MB_OK) +} + +// Errorf is generic error message with OK button. +func Errorf(parent Controller, format string, data ...interface{}) { + caption := fmt.Sprintf(format, data...) + MsgBox(parent, "Error", caption, w32.MB_ICONERROR|w32.MB_OK) +} + +func MsgBox(parent Controller, title, caption string, flags uint) int { + var result int + if parent != nil { + result = w32.MessageBox(parent.Handle(), caption, title, flags) + } else { + result = w32.MessageBox(0, caption, title, flags) + } + + return result +} diff --git a/v2/internal/frontend/desktop/windows/winc/controlbase.go b/v2/internal/frontend/desktop/windows/winc/controlbase.go new file mode 100644 index 000000000..1f7cb66ff --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/controlbase.go @@ -0,0 +1,518 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "fmt" + "runtime" + "sync" + "syscall" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type ControlBase struct { + hwnd w32.HWND + font *Font + parent Controller + contextMenu *MenuItem + + isForm bool + + minWidth, minHeight int + maxWidth, maxHeight int + + // General events + onCreate EventManager + onClose EventManager + + // Focus events + onKillFocus EventManager + onSetFocus EventManager + + // Drag and drop events + onDropFiles EventManager + + // Mouse events + onLBDown EventManager + onLBUp EventManager + onLBDbl EventManager + onMBDown EventManager + onMBUp EventManager + onRBDown EventManager + onRBUp EventManager + onRBDbl EventManager + onMouseMove EventManager + + // use MouseControl to capture onMouseHover and onMouseLeave events. + onMouseHover EventManager + onMouseLeave EventManager + + // Keyboard events + onKeyUp EventManager + + // Paint events + onPaint EventManager + onSize EventManager + + m sync.Mutex + dispatchq []func() +} + +// initControl is called by controls: edit, button, treeview, listview, and so on. +func (cba *ControlBase) InitControl(className string, parent Controller, exstyle, style uint) { + cba.hwnd = CreateWindow(className, parent, exstyle, style) + if cba.hwnd == 0 { + panic("cannot create window for " + className) + } + cba.parent = parent +} + +// InitWindow is called by custom window based controls such as split, panel, etc. +func (cba *ControlBase) InitWindow(className string, parent Controller, exstyle, style uint) { + RegClassOnlyOnce(className) + cba.hwnd = CreateWindow(className, parent, exstyle, style) + if cba.hwnd == 0 { + panic("cannot create window for " + className) + } + cba.parent = parent +} + +// SetTheme for TreeView and ListView controls. +func (cba *ControlBase) SetTheme(appName string) error { + if hr := w32.SetWindowTheme(cba.hwnd, syscall.StringToUTF16Ptr(appName), nil); w32.FAILED(hr) { + return fmt.Errorf("SetWindowTheme %d", hr) + } + return nil +} + +func (cba *ControlBase) Handle() w32.HWND { + return cba.hwnd +} + +func (cba *ControlBase) SetHandle(hwnd w32.HWND) { + cba.hwnd = hwnd +} + +func (cba *ControlBase) GetWindowDPI() (w32.UINT, w32.UINT) { + if w32.HasGetDpiForWindowFunc() { + // GetDpiForWindow is supported beginning with Windows 10, 1607 and is the most accureate + // one, especially it is consistent with the WM_DPICHANGED event. + dpi := w32.GetDpiForWindow(cba.hwnd) + return dpi, dpi + } + + if w32.HasGetDPIForMonitorFunc() { + // GetDpiForWindow is supported beginning with Windows 8.1 + monitor := w32.MonitorFromWindow(cba.hwnd, w32.MONITOR_DEFAULTTONEAREST) + if monitor == 0 { + return 0, 0 + } + var dpiX, dpiY w32.UINT + w32.GetDPIForMonitor(monitor, w32.MDT_EFFECTIVE_DPI, &dpiX, &dpiY) + return dpiX, dpiY + } + + // If none of the above is supported fallback to the System DPI. + screen := w32.GetDC(0) + x := w32.GetDeviceCaps(screen, w32.LOGPIXELSX) + y := w32.GetDeviceCaps(screen, w32.LOGPIXELSY) + w32.ReleaseDC(0, screen) + return w32.UINT(x), w32.UINT(y) +} + +func (cba *ControlBase) SetAndClearStyleBits(set, clear uint32) error { + style := uint32(w32.GetWindowLong(cba.hwnd, w32.GWL_STYLE)) + if style == 0 { + return fmt.Errorf("GetWindowLong") + } + + if newStyle := style&^clear | set; newStyle != style { + if w32.SetWindowLong(cba.hwnd, w32.GWL_STYLE, newStyle) == 0 { + return fmt.Errorf("SetWindowLong") + } + } + return nil +} + +func (cba *ControlBase) SetIsForm(isform bool) { + cba.isForm = isform +} + +func (cba *ControlBase) SetText(caption string) { + w32.SetWindowText(cba.hwnd, caption) +} + +func (cba *ControlBase) Text() string { + return w32.GetWindowText(cba.hwnd) +} + +func (cba *ControlBase) Close() { + UnRegMsgHandler(cba.hwnd) + w32.DestroyWindow(cba.hwnd) +} + +func (cba *ControlBase) SetTranslucentBackground() { + var accent = w32.ACCENT_POLICY{ + AccentState: w32.ACCENT_ENABLE_BLURBEHIND, + } + var data w32.WINDOWCOMPOSITIONATTRIBDATA + data.Attrib = w32.WCA_ACCENT_POLICY + data.PvData = unsafe.Pointer(&accent) + data.CbData = unsafe.Sizeof(accent) + + w32.SetWindowCompositionAttribute(cba.hwnd, &data) +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func (cba *ControlBase) clampSize(width, height int) (int, int) { + if cba.minWidth != 0 { + width = max(width, cba.minWidth) + } + if cba.maxWidth != 0 { + width = min(width, cba.maxWidth) + } + if cba.minHeight != 0 { + height = max(height, cba.minHeight) + } + if cba.maxHeight != 0 { + height = min(height, cba.maxHeight) + } + return width, height +} + +func (cba *ControlBase) SetSize(width, height int) { + x, y := cba.Pos() + width, height = cba.clampSize(width, height) + width, height = cba.scaleWithWindowDPI(width, height) + w32.MoveWindow(cba.hwnd, x, y, width, height, true) +} + +func (cba *ControlBase) SetMinSize(width, height int) { + cba.minWidth = width + cba.minHeight = height + + // Ensure we set max if min > max + if cba.maxWidth > 0 { + cba.maxWidth = max(cba.minWidth, cba.maxWidth) + } + if cba.maxHeight > 0 { + cba.maxHeight = max(cba.minHeight, cba.maxHeight) + } + + x, y := cba.Pos() + currentWidth, currentHeight := cba.Size() + clampedWidth, clampedHeight := cba.clampSize(currentWidth, currentHeight) + if clampedWidth != currentWidth || clampedHeight != currentHeight { + w32.MoveWindow(cba.hwnd, x, y, clampedWidth, clampedHeight, true) + } +} +func (cba *ControlBase) SetMaxSize(width, height int) { + cba.maxWidth = width + cba.maxHeight = height + + // Ensure we set min if max > min + if cba.maxWidth > 0 { + cba.minWidth = min(cba.maxWidth, cba.minWidth) + } + if cba.maxHeight > 0 { + cba.minHeight = min(cba.maxHeight, cba.minHeight) + } + + x, y := cba.Pos() + currentWidth, currentHeight := cba.Size() + clampedWidth, clampedHeight := cba.clampSize(currentWidth, currentHeight) + if clampedWidth != currentWidth || clampedHeight != currentHeight { + w32.MoveWindow(cba.hwnd, x, y, clampedWidth, clampedHeight, true) + } +} + +func (cba *ControlBase) Size() (width, height int) { + rect := w32.GetWindowRect(cba.hwnd) + width = int(rect.Right - rect.Left) + height = int(rect.Bottom - rect.Top) + return +} + +func (cba *ControlBase) Width() int { + rect := w32.GetWindowRect(cba.hwnd) + return int(rect.Right - rect.Left) +} + +func (cba *ControlBase) Height() int { + rect := w32.GetWindowRect(cba.hwnd) + return int(rect.Bottom - rect.Top) +} + +func (cba *ControlBase) SetPos(x, y int) { + info := getMonitorInfo(cba.hwnd) + workRect := info.RcWork + + w32.SetWindowPos(cba.hwnd, w32.HWND_TOP, int(workRect.Left)+x, int(workRect.Top)+y, 0, 0, w32.SWP_NOSIZE) +} + +func (cba *ControlBase) Pos() (x, y int) { + rect := w32.GetWindowRect(cba.hwnd) + x = int(rect.Left) + y = int(rect.Top) + if !cba.isForm && cba.parent != nil { + x, y, _ = w32.ScreenToClient(cba.parent.Handle(), x, y) + } + return +} + +func (cba *ControlBase) Visible() bool { + return w32.IsWindowVisible(cba.hwnd) +} + +func (cba *ControlBase) ToggleVisible() bool { + visible := w32.IsWindowVisible(cba.hwnd) + if visible { + cba.Hide() + } else { + cba.Show() + } + return !visible +} + +func (cba *ControlBase) ContextMenu() *MenuItem { + return cba.contextMenu +} + +func (cba *ControlBase) SetContextMenu(menu *MenuItem) { + cba.contextMenu = menu +} + +func (cba *ControlBase) Bounds() *Rect { + rect := w32.GetWindowRect(cba.hwnd) + if cba.isForm { + return &Rect{*rect} + } + + return ScreenToClientRect(cba.hwnd, rect) +} + +func (cba *ControlBase) ClientRect() *Rect { + rect := w32.GetClientRect(cba.hwnd) + return ScreenToClientRect(cba.hwnd, rect) +} +func (cba *ControlBase) ClientWidth() int { + rect := w32.GetClientRect(cba.hwnd) + return int(rect.Right - rect.Left) +} + +func (cba *ControlBase) ClientHeight() int { + rect := w32.GetClientRect(cba.hwnd) + return int(rect.Bottom - rect.Top) +} + +func (cba *ControlBase) Show() { + w32.ShowWindow(cba.hwnd, w32.SW_SHOWDEFAULT) +} + +func (cba *ControlBase) Hide() { + w32.ShowWindow(cba.hwnd, w32.SW_HIDE) +} + +func (cba *ControlBase) Enabled() bool { + return w32.IsWindowEnabled(cba.hwnd) +} + +func (cba *ControlBase) SetEnabled(b bool) { + w32.EnableWindow(cba.hwnd, b) +} + +func (cba *ControlBase) SetFocus() { + w32.SetFocus(cba.hwnd) +} + +func (cba *ControlBase) Invalidate(erase bool) { + // pRect := w32.GetClientRect(cba.hwnd) + // if cba.isForm { + // w32.InvalidateRect(cba.hwnd, pRect, erase) + // } else { + // rc := ScreenToClientRect(cba.hwnd, pRect) + // w32.InvalidateRect(cba.hwnd, rc.GetW32Rect(), erase) + // } + w32.InvalidateRect(cba.hwnd, nil, erase) +} + +func (cba *ControlBase) Parent() Controller { + return cba.parent +} + +func (cba *ControlBase) SetParent(parent Controller) { + cba.parent = parent +} + +func (cba *ControlBase) Font() *Font { + return cba.font +} + +func (cba *ControlBase) SetFont(font *Font) { + w32.SendMessage(cba.hwnd, w32.WM_SETFONT, uintptr(font.hfont), 1) + cba.font = font +} + +func (cba *ControlBase) EnableDragAcceptFiles(b bool) { + w32.DragAcceptFiles(cba.hwnd, b) +} + +func (cba *ControlBase) InvokeRequired() bool { + if cba.hwnd == 0 { + return false + } + + windowThreadId, _ := w32.GetWindowThreadProcessId(cba.hwnd) + currentThreadId := w32.GetCurrentThreadId() + + return windowThreadId != currentThreadId +} + +func (cba *ControlBase) Invoke(f func()) { + if cba.tryInvokeOnCurrentGoRoutine(f) { + return + } + + cba.m.Lock() + cba.dispatchq = append(cba.dispatchq, f) + cba.m.Unlock() + w32.PostMessage(cba.hwnd, wmInvokeCallback, 0, 0) +} + +func (cba *ControlBase) PreTranslateMessage(msg *w32.MSG) bool { + if msg.Message == w32.WM_GETDLGCODE { + println("pretranslate, WM_GETDLGCODE") + } + return false +} + +//Events +func (cba *ControlBase) OnCreate() *EventManager { + return &cba.onCreate +} + +func (cba *ControlBase) OnClose() *EventManager { + return &cba.onClose +} + +func (cba *ControlBase) OnKillFocus() *EventManager { + return &cba.onKillFocus +} + +func (cba *ControlBase) OnSetFocus() *EventManager { + return &cba.onSetFocus +} + +func (cba *ControlBase) OnDropFiles() *EventManager { + return &cba.onDropFiles +} + +func (cba *ControlBase) OnLBDown() *EventManager { + return &cba.onLBDown +} + +func (cba *ControlBase) OnLBUp() *EventManager { + return &cba.onLBUp +} + +func (cba *ControlBase) OnLBDbl() *EventManager { + return &cba.onLBDbl +} + +func (cba *ControlBase) OnMBDown() *EventManager { + return &cba.onMBDown +} + +func (cba *ControlBase) OnMBUp() *EventManager { + return &cba.onMBUp +} + +func (cba *ControlBase) OnRBDown() *EventManager { + return &cba.onRBDown +} + +func (cba *ControlBase) OnRBUp() *EventManager { + return &cba.onRBUp +} + +func (cba *ControlBase) OnRBDbl() *EventManager { + return &cba.onRBDbl +} + +func (cba *ControlBase) OnMouseMove() *EventManager { + return &cba.onMouseMove +} + +func (cba *ControlBase) OnMouseHover() *EventManager { + return &cba.onMouseHover +} + +func (cba *ControlBase) OnMouseLeave() *EventManager { + return &cba.onMouseLeave +} + +func (cba *ControlBase) OnPaint() *EventManager { + return &cba.onPaint +} + +func (cba *ControlBase) OnSize() *EventManager { + return &cba.onSize +} + +func (cba *ControlBase) OnKeyUp() *EventManager { + return &cba.onKeyUp +} + +func (cba *ControlBase) scaleWithWindowDPI(width, height int) (int, int) { + dpix, dpiy := cba.GetWindowDPI() + scaledWidth := ScaleWithDPI(width, dpix) + scaledHeight := ScaleWithDPI(height, dpiy) + + return scaledWidth, scaledHeight +} + +func (cba *ControlBase) tryInvokeOnCurrentGoRoutine(f func()) bool { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if cba.InvokeRequired() { + return false + } + f() + return true +} + +func (cba *ControlBase) invokeCallbacks() { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if cba.InvokeRequired() { + panic("InvokeCallbacks must always be called on the window thread") + } + + cba.m.Lock() + q := append([]func(){}, cba.dispatchq...) + cba.dispatchq = []func(){} + cba.m.Unlock() + for _, v := range q { + v() + } +} diff --git a/v2/internal/frontend/desktop/windows/winc/controller.go b/v2/internal/frontend/desktop/windows/winc/controller.go new file mode 100644 index 000000000..4fd038818 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/controller.go @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type Controller interface { + Text() string + + Enabled() bool + SetFocus() + + Handle() w32.HWND + Invalidate(erase bool) + Parent() Controller + + Pos() (x, y int) + Size() (w, h int) + Height() int + Width() int + Visible() bool + Bounds() *Rect + ClientRect() *Rect + + SetText(s string) + SetEnabled(b bool) + SetPos(x, y int) + SetSize(w, h int) + EnableDragAcceptFiles(b bool) + Show() + Hide() + + ContextMenu() *MenuItem + SetContextMenu(menu *MenuItem) + + Font() *Font + SetFont(font *Font) + InvokeRequired() bool + Invoke(func()) + PreTranslateMessage(msg *w32.MSG) bool + WndProc(msg uint32, wparam, lparam uintptr) uintptr + + //General events + OnCreate() *EventManager + OnClose() *EventManager + + // Focus events + OnKillFocus() *EventManager + OnSetFocus() *EventManager + + //Drag and drop events + OnDropFiles() *EventManager + + //Mouse events + OnLBDown() *EventManager + OnLBUp() *EventManager + OnLBDbl() *EventManager + OnMBDown() *EventManager + OnMBUp() *EventManager + OnRBDown() *EventManager + OnRBUp() *EventManager + OnRBDbl() *EventManager + OnMouseMove() *EventManager + + // OnMouseLeave and OnMouseHover does not fire unless control called internalTrackMouseEvent. + // Use MouseControl for a how to example. + OnMouseHover() *EventManager + OnMouseLeave() *EventManager + + //Keyboard events + OnKeyUp() *EventManager + + //Paint events + OnPaint() *EventManager + OnSize() *EventManager + + invokeCallbacks() +} diff --git a/v2/internal/frontend/desktop/windows/winc/dialog.go b/v2/internal/frontend/desktop/windows/winc/dialog.go new file mode 100644 index 000000000..721acbf7e --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/dialog.go @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" + +// Dialog displayed as z-order top window until closed. +// It also disables parent window so it can not be clicked. +type Dialog struct { + Form + isModal bool + + btnOk *PushButton + btnCancel *PushButton + + onLoad EventManager + onOk EventManager + onCancel EventManager +} + +func NewDialog(parent Controller) *Dialog { + dlg := new(Dialog) + + dlg.isForm = true + dlg.isModal = true + RegClassOnlyOnce("winc_Dialog") + + dlg.hwnd = CreateWindow("winc_Dialog", parent, w32.WS_EX_CONTROLPARENT, /* IMPORTANT */ + w32.WS_SYSMENU|w32.WS_CAPTION|w32.WS_THICKFRAME /*|w32.WS_BORDER|w32.WS_POPUP*/) + dlg.parent = parent + + // dlg might fail if icon resource is not embedded in the binary + if ico, err := NewIconFromResource(GetAppInstance(), uint16(AppIconID)); err == nil { + dlg.SetIcon(0, ico) + } + + // Dlg forces display of focus rectangles, as soon as the user starts to type. + w32.SendMessage(dlg.hwnd, w32.WM_CHANGEUISTATE, w32.UIS_INITIALIZE, 0) + RegMsgHandler(dlg) + + dlg.SetFont(DefaultFont) + dlg.SetText("Form") + dlg.SetSize(200, 100) + return dlg +} + +func (dlg *Dialog) SetModal(modal bool) { + dlg.isModal = modal +} + +// SetButtons wires up dialog events to buttons. btnCancel can be nil. +func (dlg *Dialog) SetButtons(btnOk *PushButton, btnCancel *PushButton) { + dlg.btnOk = btnOk + dlg.btnOk.SetDefault() + dlg.btnCancel = btnCancel +} + +// Events +func (dlg *Dialog) OnLoad() *EventManager { + return &dlg.onLoad +} + +func (dlg *Dialog) OnOk() *EventManager { + return &dlg.onOk +} + +func (dlg *Dialog) OnCancel() *EventManager { + return &dlg.onCancel +} + +// PreTranslateMessage handles dialog specific messages. IMPORTANT. +func (dlg *Dialog) PreTranslateMessage(msg *w32.MSG) bool { + if msg.Message >= w32.WM_KEYFIRST && msg.Message <= w32.WM_KEYLAST { + if w32.IsDialogMessage(dlg.hwnd, msg) { + return true + } + } + return false +} + +// Show dialog performs special setup for dialog windows. +func (dlg *Dialog) Show() { + if dlg.isModal { + dlg.Parent().SetEnabled(false) + } + dlg.onLoad.Fire(NewEvent(dlg, nil)) + dlg.Form.Show() +} + +// Close dialog when you done with it. +func (dlg *Dialog) Close() { + if dlg.isModal { + dlg.Parent().SetEnabled(true) + } + dlg.ControlBase.Close() +} + +func (dlg *Dialog) cancel() { + if dlg.btnCancel != nil { + dlg.btnCancel.onClick.Fire(NewEvent(dlg.btnCancel, nil)) + } + dlg.onCancel.Fire(NewEvent(dlg, nil)) +} + +func (dlg *Dialog) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + switch msg { + case w32.WM_COMMAND: + switch w32.LOWORD(uint32(wparam)) { + case w32.IDOK: + if dlg.btnOk != nil { + dlg.btnOk.onClick.Fire(NewEvent(dlg.btnOk, nil)) + } + dlg.onOk.Fire(NewEvent(dlg, nil)) + return w32.TRUE + + case w32.IDCANCEL: + dlg.cancel() + return w32.TRUE + } + + case w32.WM_CLOSE: + dlg.cancel() // use onCancel or dlg.btnCancel.OnClick to close + return 0 + + case w32.WM_DESTROY: + if dlg.isModal { + dlg.Parent().SetEnabled(true) + } + } + return w32.DefWindowProc(dlg.hwnd, msg, wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/dock_topbottom.png b/v2/internal/frontend/desktop/windows/winc/dock_topbottom.png new file mode 100644 index 0000000000000000000000000000000000000000..e6f1c62312f431bd40399317aed795a89031aab2 GIT binary patch literal 4373 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 tWbG+)O%ebA literal 0 HcmV?d00001 diff --git a/v2/internal/frontend/desktop/windows/winc/dock_topleft.png b/v2/internal/frontend/desktop/windows/winc/dock_topleft.png new file mode 100644 index 0000000000000000000000000000000000000000..242c93e39eb2e97f1a09fcd5c1f23fa849591467 GIT binary patch literal 3556 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 FontBold|FontItalic|FontUnderline|FontStrikeOut { + panic("Invalid font style") + } + + //Retrive screen DPI + hDC := w32.GetDC(0) + defer w32.ReleaseDC(0, hDC) + screenDPIY := w32.GetDeviceCaps(hDC, w32.LOGPIXELSY) + + font := Font{ + family: family, + pointSize: pointSize, + style: style, + } + + font.hfont = font.createForDPI(screenDPIY) + if font.hfont == 0 { + panic("CreateFontIndirect failed") + } + + return &font +} + +func (fnt *Font) createForDPI(dpi int) w32.HFONT { + var lf w32.LOGFONT + + lf.Height = int32(-w32.MulDiv(fnt.pointSize, dpi, 72)) + if fnt.style&FontBold > 0 { + lf.Weight = w32.FW_BOLD + } else { + lf.Weight = w32.FW_NORMAL + } + if fnt.style&FontItalic > 0 { + lf.Italic = 1 + } + if fnt.style&FontUnderline > 0 { + lf.Underline = 1 + } + if fnt.style&FontStrikeOut > 0 { + lf.StrikeOut = 1 + } + lf.CharSet = w32.DEFAULT_CHARSET + lf.OutPrecision = w32.OUT_TT_PRECIS + lf.ClipPrecision = w32.CLIP_DEFAULT_PRECIS + lf.Quality = w32.CLEARTYPE_QUALITY + lf.PitchAndFamily = w32.VARIABLE_PITCH | w32.FF_SWISS + + src := syscall.StringToUTF16(fnt.family) + dest := lf.FaceName[:] + copy(dest, src) + + return w32.CreateFontIndirect(&lf) +} + +func (fnt *Font) GetHFONT() w32.HFONT { + return fnt.hfont +} + +func (fnt *Font) Bold() bool { + return fnt.style&FontBold > 0 +} + +func (fnt *Font) Dispose() { + if fnt.hfont != 0 { + w32.DeleteObject(w32.HGDIOBJ(fnt.hfont)) + } +} + +func (fnt *Font) Family() string { + return fnt.family +} + +func (fnt *Font) Italic() bool { + return fnt.style&FontItalic > 0 +} + +func (fnt *Font) StrikeOut() bool { + return fnt.style&FontStrikeOut > 0 +} + +func (fnt *Font) Underline() bool { + return fnt.style&FontUnderline > 0 +} + +func (fnt *Font) Style() byte { + return fnt.style +} diff --git a/v2/internal/frontend/desktop/windows/winc/form.go b/v2/internal/frontend/desktop/windows/winc/form.go new file mode 100644 index 000000000..b146dbf29 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/form.go @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type LayoutManager interface { + Update() +} + +// A Form is main window of the application. +type Form struct { + ControlBase + + layoutMng LayoutManager + + // Fullscreen / Unfullscreen + isFullscreen bool + previousWindowStyle uint32 + previousWindowPlacement w32.WINDOWPLACEMENT +} + +func NewCustomForm(parent Controller, exStyle int, dwStyle uint) *Form { + fm := new(Form) + + RegClassOnlyOnce("winc_Form") + + fm.isForm = true + + if exStyle == 0 { + exStyle = w32.WS_EX_CONTROLPARENT | w32.WS_EX_APPWINDOW + } + + if dwStyle == 0 { + dwStyle = w32.WS_OVERLAPPEDWINDOW + } + + fm.hwnd = CreateWindow("winc_Form", parent, uint(exStyle), dwStyle) + fm.parent = parent + + // this might fail if icon resource is not embedded in the binary + if ico, err := NewIconFromResource(GetAppInstance(), uint16(AppIconID)); err == nil { + fm.SetIcon(0, ico) + } + + // This forces display of focus rectangles, as soon as the user starts to type. + w32.SendMessage(fm.hwnd, w32.WM_CHANGEUISTATE, w32.UIS_INITIALIZE, 0) + + RegMsgHandler(fm) + + fm.SetFont(DefaultFont) + fm.SetText("Form") + return fm +} + +func NewForm(parent Controller) *Form { + fm := new(Form) + + RegClassOnlyOnce("winc_Form") + + fm.isForm = true + fm.hwnd = CreateWindow("winc_Form", parent, w32.WS_EX_CONTROLPARENT|w32.WS_EX_APPWINDOW, w32.WS_OVERLAPPEDWINDOW) + fm.parent = parent + + // this might fail if icon resource is not embedded in the binary + if ico, err := NewIconFromResource(GetAppInstance(), uint16(AppIconID)); err == nil { + fm.SetIcon(0, ico) + } + + // This forces display of focus rectangles, as soon as the user starts to type. + w32.SendMessage(fm.hwnd, w32.WM_CHANGEUISTATE, w32.UIS_INITIALIZE, 0) + + RegMsgHandler(fm) + + fm.SetFont(DefaultFont) + fm.SetText("Form") + return fm +} + +func (fm *Form) SetLayout(mng LayoutManager) { + fm.layoutMng = mng +} + +// UpdateLayout refresh layout. +func (fm *Form) UpdateLayout() { + if fm.layoutMng != nil { + fm.layoutMng.Update() + } +} + +func (fm *Form) NewMenu() *Menu { + hMenu := w32.CreateMenu() + if hMenu == 0 { + panic("failed CreateMenu") + } + m := &Menu{hMenu: hMenu, hwnd: fm.hwnd} + if !w32.SetMenu(fm.hwnd, hMenu) { + panic("failed SetMenu") + } + return m +} + +func (fm *Form) DisableIcon() { + windowInfo := getWindowInfo(fm.hwnd) + frameless := windowInfo.IsPopup() + if frameless { + return + } + exStyle := w32.GetWindowLong(fm.hwnd, w32.GWL_EXSTYLE) + w32.SetWindowLong(fm.hwnd, w32.GWL_EXSTYLE, uint32(exStyle|w32.WS_EX_DLGMODALFRAME)) + w32.SetWindowPos(fm.hwnd, 0, 0, 0, 0, 0, + uint( + w32.SWP_FRAMECHANGED| + w32.SWP_NOMOVE| + w32.SWP_NOSIZE| + w32.SWP_NOZORDER), + ) +} + +func (fm *Form) Maximise() { + w32.ShowWindow(fm.hwnd, w32.SW_MAXIMIZE) +} + +func (fm *Form) Minimise() { + w32.ShowWindow(fm.hwnd, w32.SW_MINIMIZE) +} + +func (fm *Form) Restore() { + w32.ShowWindow(fm.hwnd, w32.SW_RESTORE) +} + +// Public methods +func (fm *Form) Center() { + + windowInfo := getWindowInfo(fm.hwnd) + frameless := windowInfo.IsPopup() + + info := getMonitorInfo(fm.hwnd) + workRect := info.RcWork + screenMiddleW := workRect.Left + (workRect.Right-workRect.Left)/2 + screenMiddleH := workRect.Top + (workRect.Bottom-workRect.Top)/2 + var winRect *w32.RECT + if !frameless { + winRect = w32.GetWindowRect(fm.hwnd) + } else { + winRect = w32.GetClientRect(fm.hwnd) + } + winWidth := winRect.Right - winRect.Left + winHeight := winRect.Bottom - winRect.Top + windowX := screenMiddleW - (winWidth / 2) + windowY := screenMiddleH - (winHeight / 2) + w32.SetWindowPos(fm.hwnd, w32.HWND_TOP, int(windowX), int(windowY), int(winWidth), int(winHeight), w32.SWP_NOSIZE) +} + +func (fm *Form) Fullscreen() { + if fm.isFullscreen { + return + } + + fm.previousWindowStyle = uint32(w32.GetWindowLongPtr(fm.hwnd, w32.GWL_STYLE)) + monitor := w32.MonitorFromWindow(fm.hwnd, w32.MONITOR_DEFAULTTOPRIMARY) + var monitorInfo w32.MONITORINFO + monitorInfo.CbSize = uint32(unsafe.Sizeof(monitorInfo)) + if !w32.GetMonitorInfo(monitor, &monitorInfo) { + return + } + if !w32.GetWindowPlacement(fm.hwnd, &fm.previousWindowPlacement) { + return + } + w32.SetWindowLong(fm.hwnd, w32.GWL_STYLE, fm.previousWindowStyle & ^uint32(w32.WS_OVERLAPPEDWINDOW)) + w32.SetWindowPos(fm.hwnd, w32.HWND_TOP, + int(monitorInfo.RcMonitor.Left), + int(monitorInfo.RcMonitor.Top), + int(monitorInfo.RcMonitor.Right-monitorInfo.RcMonitor.Left), + int(monitorInfo.RcMonitor.Bottom-monitorInfo.RcMonitor.Top), + w32.SWP_NOOWNERZORDER|w32.SWP_FRAMECHANGED) + fm.isFullscreen = true +} + +func (fm *Form) UnFullscreen() { + if !fm.isFullscreen { + return + } + w32.SetWindowLong(fm.hwnd, w32.GWL_STYLE, fm.previousWindowStyle) + w32.SetWindowPlacement(fm.hwnd, &fm.previousWindowPlacement) + w32.SetWindowPos(fm.hwnd, 0, 0, 0, 0, 0, + w32.SWP_NOMOVE|w32.SWP_NOSIZE|w32.SWP_NOZORDER|w32.SWP_NOOWNERZORDER|w32.SWP_FRAMECHANGED) + fm.isFullscreen = false +} + +func (fm *Form) IsFullScreen() bool { + return fm.isFullscreen +} + +// IconType: 1 - ICON_BIG; 0 - ICON_SMALL +func (fm *Form) SetIcon(iconType int, icon *Icon) { + if iconType > 1 { + panic("IconType is invalid") + } + w32.SendMessage(fm.hwnd, w32.WM_SETICON, uintptr(iconType), uintptr(icon.Handle())) +} + +func (fm *Form) EnableMaxButton(b bool) { + SetStyle(fm.hwnd, b, w32.WS_MAXIMIZEBOX) +} + +func (fm *Form) EnableMinButton(b bool) { + SetStyle(fm.hwnd, b, w32.WS_MINIMIZEBOX) +} + +func (fm *Form) EnableSizable(b bool) { + SetStyle(fm.hwnd, b, w32.WS_THICKFRAME) +} + +func (fm *Form) EnableDragMove(_ bool) { + //fm.isDragMove = b +} + +func (fm *Form) EnableTopMost(b bool) { + tag := w32.HWND_NOTOPMOST + if b { + tag = w32.HWND_TOPMOST + } + w32.SetWindowPos(fm.hwnd, tag, 0, 0, 0, 0, w32.SWP_NOMOVE|w32.SWP_NOSIZE) +} + +func (fm *Form) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + + switch msg { + case w32.WM_COMMAND: + if lparam == 0 && w32.HIWORD(uint32(wparam)) == 0 { + // Menu support. + actionID := uint16(w32.LOWORD(uint32(wparam))) + if action, ok := actionsByID[actionID]; ok { + action.onClick.Fire(NewEvent(fm, nil)) + } + } + case w32.WM_KEYDOWN: + // Accelerator support. + key := Key(wparam) + if uint32(lparam)>>30 == 0 { + // Using TranslateAccelerators refused to work, so we handle them + // ourselves, at least for now. + shortcut := Shortcut{ModifiersDown(), key} + if action, ok := shortcut2Action[shortcut]; ok { + if action.Enabled() { + action.onClick.Fire(NewEvent(fm, nil)) + } + } + } + + case w32.WM_CLOSE: + return 0 + case w32.WM_DESTROY: + w32.PostQuitMessage(0) + return 0 + + case w32.WM_SIZE, w32.WM_PAINT: + if fm.layoutMng != nil { + fm.layoutMng.Update() + } + case w32.WM_GETMINMAXINFO: + mmi := (*w32.MINMAXINFO)(unsafe.Pointer(lparam)) + hasConstraints := false + if fm.minWidth > 0 || fm.minHeight > 0 { + hasConstraints = true + + width, height := fm.scaleWithWindowDPI(fm.minWidth, fm.minHeight) + if width > 0 { + mmi.PtMinTrackSize.X = int32(width) + } + if height > 0 { + mmi.PtMinTrackSize.Y = int32(height) + } + } + if fm.maxWidth > 0 || fm.maxHeight > 0 { + hasConstraints = true + + width, height := fm.scaleWithWindowDPI(fm.maxWidth, fm.maxHeight) + if width > 0 { + mmi.PtMaxTrackSize.X = int32(width) + } + if height > 0 { + mmi.PtMaxTrackSize.Y = int32(height) + } + } + if hasConstraints { + return 0 + } + } + + return w32.DefWindowProc(fm.hwnd, msg, wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/globalvars.go b/v2/internal/frontend/desktop/windows/winc/globalvars.go new file mode 100644 index 000000000..c3da17a6a --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/globalvars.go @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "syscall" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +//Private global variables. +var ( + gAppInstance w32.HINSTANCE + gControllerRegistry map[w32.HWND]Controller + gRegisteredClasses []string +) + +//Public global variables. +var ( + GeneralWndprocCallBack = syscall.NewCallback(generalWndProc) + DefaultFont *Font +) diff --git a/v2/internal/frontend/desktop/windows/winc/icon.go b/v2/internal/frontend/desktop/windows/winc/icon.go new file mode 100644 index 000000000..97bc79b2c --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/icon.go @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "errors" + "fmt" + "syscall" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type Icon struct { + handle w32.HICON +} + +func NewIconFromFile(path string) (*Icon, error) { + ico := new(Icon) + var err error + if ico.handle = w32.LoadIcon(0, syscall.StringToUTF16Ptr(path)); ico.handle == 0 { + err = errors.New(fmt.Sprintf("Cannot load icon from %s", path)) + } + return ico, err +} + +func NewIconFromResource(instance w32.HINSTANCE, resId uint16) (*Icon, error) { + ico := new(Icon) + var err error + if ico.handle = w32.LoadIcon(instance, w32.MakeIntResource(resId)); ico.handle == 0 { + err = errors.New(fmt.Sprintf("Cannot load icon from resource with id %v", resId)) + } + return ico, err +} + +func ExtractIcon(fileName string, index int) (*Icon, error) { + ico := new(Icon) + var err error + if ico.handle = w32.ExtractIcon(fileName, index); ico.handle == 0 || ico.handle == 1 { + err = errors.New(fmt.Sprintf("Cannot extract icon from %s at index %v", fileName, index)) + } + return ico, err +} + +func (ic *Icon) Destroy() bool { + return w32.DestroyIcon(ic.handle) +} + +func (ic *Icon) Handle() w32.HICON { + return ic.handle +} diff --git a/v2/internal/frontend/desktop/windows/winc/imagelist.go b/v2/internal/frontend/desktop/windows/winc/imagelist.go new file mode 100644 index 000000000..45bf32f80 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/imagelist.go @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "fmt" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type ImageList struct { + handle w32.HIMAGELIST +} + +func NewImageList(cx, cy int) *ImageList { + return newImageList(cx, cy, w32.ILC_COLOR32, 0, 0) +} + +func newImageList(cx, cy int, flags uint, cInitial, cGrow int) *ImageList { + imgl := new(ImageList) + imgl.handle = w32.ImageList_Create(cx, cy, flags, cInitial, cGrow) + return imgl +} + +func (im *ImageList) Handle() w32.HIMAGELIST { + return im.handle +} + +func (im *ImageList) Destroy() bool { + return w32.ImageList_Destroy(im.handle) +} + +func (im *ImageList) SetImageCount(uNewCount uint) bool { + return w32.ImageList_SetImageCount(im.handle, uNewCount) +} + +func (im *ImageList) ImageCount() int { + return w32.ImageList_GetImageCount(im.handle) +} + +func (im *ImageList) AddIcon(icon *Icon) int { + return w32.ImageList_AddIcon(im.handle, icon.Handle()) +} + +func (im *ImageList) AddResIcon(iconID uint16) { + if ico, err := NewIconFromResource(GetAppInstance(), iconID); err == nil { + im.AddIcon(ico) + return + } + panic(fmt.Sprintf("missing icon with icon ID: %d", iconID)) +} + +func (im *ImageList) RemoveAll() bool { + return w32.ImageList_RemoveAll(im.handle) +} + +func (im *ImageList) Remove(i int) bool { + return w32.ImageList_Remove(im.handle, i) +} diff --git a/v2/internal/frontend/desktop/windows/winc/imageview.go b/v2/internal/frontend/desktop/windows/winc/imageview.go new file mode 100644 index 000000000..acd4856f8 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/imageview.go @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + */ + +package winc + +import "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" + +type ImageView struct { + ControlBase + + bmp *Bitmap +} + +func NewImageView(parent Controller) *ImageView { + iv := new(ImageView) + + iv.InitWindow("winc_ImageView", parent, w32.WS_EX_CONTROLPARENT, w32.WS_CHILD|w32.WS_VISIBLE) + RegMsgHandler(iv) + + iv.SetFont(DefaultFont) + iv.SetText("") + iv.SetSize(200, 65) + return iv +} + +func (iv *ImageView) DrawImageFile(filepath string) error { + bmp, err := NewBitmapFromFile(filepath, RGB(255, 255, 0)) + if err != nil { + return err + } + iv.bmp = bmp + return nil +} + +func (iv *ImageView) DrawImage(bmp *Bitmap) { + iv.bmp = bmp +} + +func (iv *ImageView) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + switch msg { + case w32.WM_SIZE, w32.WM_SIZING: + iv.Invalidate(true) + + case w32.WM_ERASEBKGND: + return 1 // important + + case w32.WM_PAINT: + if iv.bmp != nil { + canvas := NewCanvasFromHwnd(iv.hwnd) + defer canvas.Dispose() + iv.SetSize(iv.bmp.Size()) + canvas.DrawBitmap(iv.bmp, 0, 0) + } + } + return w32.DefWindowProc(iv.hwnd, msg, wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/imageviewbox.go b/v2/internal/frontend/desktop/windows/winc/imageviewbox.go new file mode 100644 index 000000000..be97a4435 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/imageviewbox.go @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + */ + +package winc + +import ( + "fmt" + "time" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type direction int + +const ( + DirNone direction = iota + DirX + DirY + DirX2 + DirY2 +) + +var ImageBoxPen = NewPen(w32.PS_GEOMETRIC, 2, NewSolidColorBrush(RGB(140, 140, 220))) +var ImageBoxHiPen = NewPen(w32.PS_GEOMETRIC, 2, NewSolidColorBrush(RGB(220, 140, 140))) +var ImageBoxMarkBrush = NewSolidColorBrush(RGB(40, 40, 40)) +var ImageBoxMarkPen = NewPen(w32.PS_GEOMETRIC, 2, ImageBoxMarkBrush) + +type ImageBox struct { + Name string + Type int + X, Y, X2, Y2 int + + underMouse bool // dynamic value +} + +func (b *ImageBox) Rect() *Rect { + return NewRect(b.X, b.Y, b.X2, b.Y2) +} + +// ImageViewBox is image view with boxes. +type ImageViewBox struct { + ControlBase + + bmp *Bitmap + mouseLeft bool + modified bool // used by GUI to see if any image box modified + + add bool + + Boxes []*ImageBox // might be persisted to file + dragBox *ImageBox + selBox *ImageBox + + dragStartX, dragStartY int + resize direction + + onSelectedChange EventManager + onAdd EventManager + onModify EventManager +} + +func NewImageViewBox(parent Controller) *ImageViewBox { + iv := new(ImageViewBox) + + iv.InitWindow("winc_ImageViewBox", parent, w32.WS_EX_CONTROLPARENT, w32.WS_CHILD|w32.WS_VISIBLE) + RegMsgHandler(iv) + + iv.SetFont(DefaultFont) + iv.SetText("") + iv.SetSize(200, 65) + + return iv +} + +func (iv *ImageViewBox) OnSelectedChange() *EventManager { + return &iv.onSelectedChange +} + +func (iv *ImageViewBox) OnAdd() *EventManager { + return &iv.onAdd +} + +func (iv *ImageViewBox) OnModify() *EventManager { + return &iv.onModify +} + +func (iv *ImageViewBox) IsModified() bool { return iv.modified } +func (iv *ImageViewBox) SetModified(modified bool) { iv.modified = modified } +func (iv *ImageViewBox) IsLoaded() bool { return iv.bmp != nil } +func (iv *ImageViewBox) AddMode() bool { return iv.add } +func (iv *ImageViewBox) SetAddMode(add bool) { iv.add = add } +func (iv *ImageViewBox) HasSelected() bool { return iv.selBox != nil && iv.bmp != nil } + +func (iv *ImageViewBox) wasModified() { + iv.modified = true + iv.onModify.Fire(NewEvent(iv, nil)) +} + +func (iv *ImageViewBox) DeleteSelected() { + if iv.selBox != nil { + for i, b := range iv.Boxes { + if b == iv.selBox { + iv.Boxes = append(iv.Boxes[:i], iv.Boxes[i+1:]...) + iv.selBox = nil + iv.Invalidate(true) + iv.wasModified() + iv.onSelectedChange.Fire(NewEvent(iv, nil)) + return + } + } + } +} + +func (iv *ImageViewBox) NameSelected() string { + if iv.selBox != nil { + return iv.selBox.Name + } + return "" +} + +func (iv *ImageViewBox) SetNameSelected(name string) { + if iv.selBox != nil { + iv.selBox.Name = name + iv.wasModified() + } +} + +func (iv *ImageViewBox) TypeSelected() int { + if iv.selBox != nil { + return iv.selBox.Type + } + return 0 +} + +func (iv *ImageViewBox) SetTypeSelected(typ int) { + if iv.selBox != nil { + iv.selBox.Type = typ + iv.wasModified() + } +} + +func (ib *ImageViewBox) updateHighlight(x, y int) bool { + var changed bool + for _, b := range ib.Boxes { + under := x >= b.X && y >= b.Y && x <= b.X2 && y <= b.Y2 + if b.underMouse != under { + changed = true + } + b.underMouse = under + /*if sel { + break // allow only one to be underMouse + }*/ + } + return changed +} + +func (ib *ImageViewBox) isUnderMouse(x, y int) *ImageBox { + for _, b := range ib.Boxes { + if x >= b.X && y >= b.Y && x <= b.X2 && y <= b.Y2 { + return b + } + } + return nil +} + +func (ib *ImageViewBox) getCursor(x, y int) uint16 { + for _, b := range ib.Boxes { + switch d := ib.resizingDirection(b, x, y); d { + case DirY, DirY2: + return w32.IDC_SIZENS + case DirX, DirX2: + return w32.IDC_SIZEWE + } + // w32.IDC_SIZEALL or w32.IDC_SIZE for resize + } + return w32.IDC_ARROW +} + +func (ib *ImageViewBox) resizingDirection(b *ImageBox, x, y int) direction { + if b == nil { + return DirNone + } + switch { + case b.X == x || b.X == x-1 || b.X == x+1: + return DirX + case b.X2 == x || b.X2 == x-1 || b.X2 == x+1: + return DirX2 + case b.Y == y || b.Y == y-1 || b.Y == y+1: + return DirY + case b.Y2 == y || b.Y2 == y-1 || b.Y2 == y+1: + return DirY2 + } + return DirNone +} + +func (ib *ImageViewBox) resizeToDirection(b *ImageBox, x, y int) { + switch ib.resize { + case DirX: + b.X = x + case DirY: + b.Y = y + case DirX2: + b.X2 = x + case DirY2: + b.Y2 = y + } +} + +func (ib *ImageViewBox) drag(b *ImageBox, x, y int) { + w, h := b.X2-b.X, b.Y2-b.Y + + nx := ib.dragStartX - b.X + ny := ib.dragStartY - b.Y + + b.X = x - nx + b.Y = y - ny + b.X2 = b.X + w + b.Y2 = b.Y + h + + ib.dragStartX, ib.dragStartY = x, y +} + +func (iv *ImageViewBox) DrawImageFile(filepath string) (err error) { + iv.bmp, err = NewBitmapFromFile(filepath, RGB(255, 255, 0)) + iv.selBox = nil + iv.modified = false + iv.onSelectedChange.Fire(NewEvent(iv, nil)) + iv.onModify.Fire(NewEvent(iv, nil)) + return +} + +func (iv *ImageViewBox) DrawImage(bmp *Bitmap) { + iv.bmp = bmp + iv.selBox = nil + iv.modified = false + iv.onSelectedChange.Fire(NewEvent(iv, nil)) + iv.onModify.Fire(NewEvent(iv, nil)) +} + +func (iv *ImageViewBox) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + switch msg { + case w32.WM_SIZE, w32.WM_SIZING: + iv.Invalidate(true) + + case w32.WM_ERASEBKGND: + return 1 // important + + case w32.WM_CREATE: + internalTrackMouseEvent(iv.hwnd) + + case w32.WM_PAINT: + if iv.bmp != nil { + canvas := NewCanvasFromHwnd(iv.hwnd) + defer canvas.Dispose() + iv.SetSize(iv.bmp.Size()) + canvas.DrawBitmap(iv.bmp, 0, 0) + + for _, b := range iv.Boxes { + // old code used NewSystemColorBrush(w32.COLOR_BTNFACE) w32.COLOR_WINDOW + pen := ImageBoxPen + if b.underMouse { + pen = ImageBoxHiPen + } + canvas.DrawRect(b.Rect(), pen) + + if b == iv.selBox { + x1 := []int{b.X, b.X2, b.X2, b.X} + y1 := []int{b.Y, b.Y, b.Y2, b.Y2} + + for i := 0; i < len(x1); i++ { + r := NewRect(x1[i]-2, y1[i]-2, x1[i]+2, y1[i]+2) + canvas.DrawFillRect(r, ImageBoxMarkPen, ImageBoxMarkBrush) + } + + } + } + } + + case w32.WM_MOUSEMOVE: + x, y := genPoint(lparam) + + if iv.dragBox != nil { + if iv.resize == DirNone { + iv.drag(iv.dragBox, x, y) + iv.wasModified() + } else { + iv.resizeToDirection(iv.dragBox, x, y) + iv.wasModified() + } + iv.Invalidate(true) + + } else { + if !iv.add { + w32.SetCursor(w32.LoadCursor(0, w32.MakeIntResource(iv.getCursor(x, y)))) + } + // do not call repaint if underMouse item did not change. + if iv.updateHighlight(x, y) { + iv.Invalidate(true) + } + } + + if iv.mouseLeft { + internalTrackMouseEvent(iv.hwnd) + iv.mouseLeft = false + } + + case w32.WM_MOUSELEAVE: + iv.dragBox = nil + iv.mouseLeft = true + iv.updateHighlight(-1, -1) + iv.Invalidate(true) + + case w32.WM_LBUTTONUP: + iv.dragBox = nil + + case w32.WM_LBUTTONDOWN: + x, y := genPoint(lparam) + if iv.add { + now := time.Now() + s := fmt.Sprintf("field%s", now.Format("020405")) + b := &ImageBox{Name: s, underMouse: true, X: x, Y: y, X2: x + 150, Y2: y + 30} + iv.Boxes = append(iv.Boxes, b) + iv.selBox = b + iv.wasModified() + iv.onAdd.Fire(NewEvent(iv, nil)) + } else { + iv.dragBox = iv.isUnderMouse(x, y) + iv.selBox = iv.dragBox + iv.dragStartX, iv.dragStartY = x, y + iv.resize = iv.resizingDirection(iv.dragBox, x, y) + } + iv.Invalidate(true) + iv.onSelectedChange.Fire(NewEvent(iv, nil)) + + case w32.WM_RBUTTONDOWN: + + } + return w32.DefWindowProc(iv.hwnd, msg, wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/init.go b/v2/internal/frontend/desktop/windows/winc/init.go new file mode 100644 index 000000000..8ec9e80c0 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/init.go @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +func init() { + gControllerRegistry = make(map[w32.HWND]Controller) + gRegisteredClasses = make([]string, 0) + + var si w32.GdiplusStartupInput + si.GdiplusVersion = 1 + w32.GdiplusStartup(&si, nil) +} diff --git a/v2/internal/frontend/desktop/windows/winc/keyboard.go b/v2/internal/frontend/desktop/windows/winc/keyboard.go new file mode 100644 index 000000000..ea38fd986 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/keyboard.go @@ -0,0 +1,438 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "bytes" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type Key uint16 + +func (k Key) String() string { + return key2string[k] +} + +const ( + KeyLButton Key = w32.VK_LBUTTON + KeyRButton Key = w32.VK_RBUTTON + KeyCancel Key = w32.VK_CANCEL + KeyMButton Key = w32.VK_MBUTTON + KeyXButton1 Key = w32.VK_XBUTTON1 + KeyXButton2 Key = w32.VK_XBUTTON2 + KeyBack Key = w32.VK_BACK + KeyTab Key = w32.VK_TAB + KeyClear Key = w32.VK_CLEAR + KeyReturn Key = w32.VK_RETURN + KeyShift Key = w32.VK_SHIFT + KeyControl Key = w32.VK_CONTROL + KeyAlt Key = w32.VK_MENU + KeyMenu Key = w32.VK_MENU + KeyPause Key = w32.VK_PAUSE + KeyCapital Key = w32.VK_CAPITAL + KeyKana Key = w32.VK_KANA + KeyHangul Key = w32.VK_HANGUL + KeyJunja Key = w32.VK_JUNJA + KeyFinal Key = w32.VK_FINAL + KeyHanja Key = w32.VK_HANJA + KeyKanji Key = w32.VK_KANJI + KeyEscape Key = w32.VK_ESCAPE + KeyConvert Key = w32.VK_CONVERT + KeyNonconvert Key = w32.VK_NONCONVERT + KeyAccept Key = w32.VK_ACCEPT + KeyModeChange Key = w32.VK_MODECHANGE + KeySpace Key = w32.VK_SPACE + KeyPrior Key = w32.VK_PRIOR + KeyNext Key = w32.VK_NEXT + KeyEnd Key = w32.VK_END + KeyHome Key = w32.VK_HOME + KeyLeft Key = w32.VK_LEFT + KeyUp Key = w32.VK_UP + KeyRight Key = w32.VK_RIGHT + KeyDown Key = w32.VK_DOWN + KeySelect Key = w32.VK_SELECT + KeyPrint Key = w32.VK_PRINT + KeyExecute Key = w32.VK_EXECUTE + KeySnapshot Key = w32.VK_SNAPSHOT + KeyInsert Key = w32.VK_INSERT + KeyDelete Key = w32.VK_DELETE + KeyHelp Key = w32.VK_HELP + Key0 Key = 0x30 + Key1 Key = 0x31 + Key2 Key = 0x32 + Key3 Key = 0x33 + Key4 Key = 0x34 + Key5 Key = 0x35 + Key6 Key = 0x36 + Key7 Key = 0x37 + Key8 Key = 0x38 + Key9 Key = 0x39 + KeyA Key = 0x41 + KeyB Key = 0x42 + KeyC Key = 0x43 + KeyD Key = 0x44 + KeyE Key = 0x45 + KeyF Key = 0x46 + KeyG Key = 0x47 + KeyH Key = 0x48 + KeyI Key = 0x49 + KeyJ Key = 0x4A + KeyK Key = 0x4B + KeyL Key = 0x4C + KeyM Key = 0x4D + KeyN Key = 0x4E + KeyO Key = 0x4F + KeyP Key = 0x50 + KeyQ Key = 0x51 + KeyR Key = 0x52 + KeyS Key = 0x53 + KeyT Key = 0x54 + KeyU Key = 0x55 + KeyV Key = 0x56 + KeyW Key = 0x57 + KeyX Key = 0x58 + KeyY Key = 0x59 + KeyZ Key = 0x5A + KeyLWIN Key = w32.VK_LWIN + KeyRWIN Key = w32.VK_RWIN + KeyApps Key = w32.VK_APPS + KeySleep Key = w32.VK_SLEEP + KeyNumpad0 Key = w32.VK_NUMPAD0 + KeyNumpad1 Key = w32.VK_NUMPAD1 + KeyNumpad2 Key = w32.VK_NUMPAD2 + KeyNumpad3 Key = w32.VK_NUMPAD3 + KeyNumpad4 Key = w32.VK_NUMPAD4 + KeyNumpad5 Key = w32.VK_NUMPAD5 + KeyNumpad6 Key = w32.VK_NUMPAD6 + KeyNumpad7 Key = w32.VK_NUMPAD7 + KeyNumpad8 Key = w32.VK_NUMPAD8 + KeyNumpad9 Key = w32.VK_NUMPAD9 + KeyMultiply Key = w32.VK_MULTIPLY + KeyAdd Key = w32.VK_ADD + KeySeparator Key = w32.VK_SEPARATOR + KeySubtract Key = w32.VK_SUBTRACT + KeyDecimal Key = w32.VK_DECIMAL + KeyDivide Key = w32.VK_DIVIDE + KeyF1 Key = w32.VK_F1 + KeyF2 Key = w32.VK_F2 + KeyF3 Key = w32.VK_F3 + KeyF4 Key = w32.VK_F4 + KeyF5 Key = w32.VK_F5 + KeyF6 Key = w32.VK_F6 + KeyF7 Key = w32.VK_F7 + KeyF8 Key = w32.VK_F8 + KeyF9 Key = w32.VK_F9 + KeyF10 Key = w32.VK_F10 + KeyF11 Key = w32.VK_F11 + KeyF12 Key = w32.VK_F12 + KeyF13 Key = w32.VK_F13 + KeyF14 Key = w32.VK_F14 + KeyF15 Key = w32.VK_F15 + KeyF16 Key = w32.VK_F16 + KeyF17 Key = w32.VK_F17 + KeyF18 Key = w32.VK_F18 + KeyF19 Key = w32.VK_F19 + KeyF20 Key = w32.VK_F20 + KeyF21 Key = w32.VK_F21 + KeyF22 Key = w32.VK_F22 + KeyF23 Key = w32.VK_F23 + KeyF24 Key = w32.VK_F24 + KeyNumlock Key = w32.VK_NUMLOCK + KeyScroll Key = w32.VK_SCROLL + KeyLShift Key = w32.VK_LSHIFT + KeyRShift Key = w32.VK_RSHIFT + KeyLControl Key = w32.VK_LCONTROL + KeyRControl Key = w32.VK_RCONTROL + KeyLAlt Key = w32.VK_LMENU + KeyLMenu Key = w32.VK_LMENU + KeyRAlt Key = w32.VK_RMENU + KeyRMenu Key = w32.VK_RMENU + KeyBrowserBack Key = w32.VK_BROWSER_BACK + KeyBrowserForward Key = w32.VK_BROWSER_FORWARD + KeyBrowserRefresh Key = w32.VK_BROWSER_REFRESH + KeyBrowserStop Key = w32.VK_BROWSER_STOP + KeyBrowserSearch Key = w32.VK_BROWSER_SEARCH + KeyBrowserFavorites Key = w32.VK_BROWSER_FAVORITES + KeyBrowserHome Key = w32.VK_BROWSER_HOME + KeyVolumeMute Key = w32.VK_VOLUME_MUTE + KeyVolumeDown Key = w32.VK_VOLUME_DOWN + KeyVolumeUp Key = w32.VK_VOLUME_UP + KeyMediaNextTrack Key = w32.VK_MEDIA_NEXT_TRACK + KeyMediaPrevTrack Key = w32.VK_MEDIA_PREV_TRACK + KeyMediaStop Key = w32.VK_MEDIA_STOP + KeyMediaPlayPause Key = w32.VK_MEDIA_PLAY_PAUSE + KeyLaunchMail Key = w32.VK_LAUNCH_MAIL + KeyLaunchMediaSelect Key = w32.VK_LAUNCH_MEDIA_SELECT + KeyLaunchApp1 Key = w32.VK_LAUNCH_APP1 + KeyLaunchApp2 Key = w32.VK_LAUNCH_APP2 + KeyOEM1 Key = w32.VK_OEM_1 + KeyOEMPlus Key = w32.VK_OEM_PLUS + KeyOEMComma Key = w32.VK_OEM_COMMA + KeyOEMMinus Key = w32.VK_OEM_MINUS + KeyOEMPeriod Key = w32.VK_OEM_PERIOD + KeyOEM2 Key = w32.VK_OEM_2 + KeyOEM3 Key = w32.VK_OEM_3 + KeyOEM4 Key = w32.VK_OEM_4 + KeyOEM5 Key = w32.VK_OEM_5 + KeyOEM6 Key = w32.VK_OEM_6 + KeyOEM7 Key = w32.VK_OEM_7 + KeyOEM8 Key = w32.VK_OEM_8 + KeyOEM102 Key = w32.VK_OEM_102 + KeyProcessKey Key = w32.VK_PROCESSKEY + KeyPacket Key = w32.VK_PACKET + KeyAttn Key = w32.VK_ATTN + KeyCRSel Key = w32.VK_CRSEL + KeyEXSel Key = w32.VK_EXSEL + KeyErEOF Key = w32.VK_EREOF + KeyPlay Key = w32.VK_PLAY + KeyZoom Key = w32.VK_ZOOM + KeyNoName Key = w32.VK_NONAME + KeyPA1 Key = w32.VK_PA1 + KeyOEMClear Key = w32.VK_OEM_CLEAR +) + +var key2string = map[Key]string{ + KeyLButton: "LButton", + KeyRButton: "RButton", + KeyCancel: "Cancel", + KeyMButton: "MButton", + KeyXButton1: "XButton1", + KeyXButton2: "XButton2", + KeyBack: "Back", + KeyTab: "Tab", + KeyClear: "Clear", + KeyReturn: "Return", + KeyShift: "Shift", + KeyControl: "Control", + KeyAlt: "Alt / Menu", + KeyPause: "Pause", + KeyCapital: "Capital", + KeyKana: "Kana / Hangul", + KeyJunja: "Junja", + KeyFinal: "Final", + KeyHanja: "Hanja / Kanji", + KeyEscape: "Escape", + KeyConvert: "Convert", + KeyNonconvert: "Nonconvert", + KeyAccept: "Accept", + KeyModeChange: "ModeChange", + KeySpace: "Space", + KeyPrior: "Prior", + KeyNext: "Next", + KeyEnd: "End", + KeyHome: "Home", + KeyLeft: "Left", + KeyUp: "Up", + KeyRight: "Right", + KeyDown: "Down", + KeySelect: "Select", + KeyPrint: "Print", + KeyExecute: "Execute", + KeySnapshot: "Snapshot", + KeyInsert: "Insert", + KeyDelete: "Delete", + KeyHelp: "Help", + Key0: "0", + Key1: "1", + Key2: "2", + Key3: "3", + Key4: "4", + Key5: "5", + Key6: "6", + Key7: "7", + Key8: "8", + Key9: "9", + KeyA: "A", + KeyB: "B", + KeyC: "C", + KeyD: "D", + KeyE: "E", + KeyF: "F", + KeyG: "G", + KeyH: "H", + KeyI: "I", + KeyJ: "J", + KeyK: "K", + KeyL: "L", + KeyM: "M", + KeyN: "N", + KeyO: "O", + KeyP: "P", + KeyQ: "Q", + KeyR: "R", + KeyS: "S", + KeyT: "T", + KeyU: "U", + KeyV: "V", + KeyW: "W", + KeyX: "X", + KeyY: "Y", + KeyZ: "Z", + KeyLWIN: "LWIN", + KeyRWIN: "RWIN", + KeyApps: "Apps", + KeySleep: "Sleep", + KeyNumpad0: "Numpad0", + KeyNumpad1: "Numpad1", + KeyNumpad2: "Numpad2", + KeyNumpad3: "Numpad3", + KeyNumpad4: "Numpad4", + KeyNumpad5: "Numpad5", + KeyNumpad6: "Numpad6", + KeyNumpad7: "Numpad7", + KeyNumpad8: "Numpad8", + KeyNumpad9: "Numpad9", + KeyMultiply: "Multiply", + KeyAdd: "Add", + KeySeparator: "Separator", + KeySubtract: "Subtract", + KeyDecimal: "Decimal", + KeyDivide: "Divide", + KeyF1: "F1", + KeyF2: "F2", + KeyF3: "F3", + KeyF4: "F4", + KeyF5: "F5", + KeyF6: "F6", + KeyF7: "F7", + KeyF8: "F8", + KeyF9: "F9", + KeyF10: "F10", + KeyF11: "F11", + KeyF12: "F12", + KeyF13: "F13", + KeyF14: "F14", + KeyF15: "F15", + KeyF16: "F16", + KeyF17: "F17", + KeyF18: "F18", + KeyF19: "F19", + KeyF20: "F20", + KeyF21: "F21", + KeyF22: "F22", + KeyF23: "F23", + KeyF24: "F24", + KeyNumlock: "Numlock", + KeyScroll: "Scroll", + KeyLShift: "LShift", + KeyRShift: "RShift", + KeyLControl: "LControl", + KeyRControl: "RControl", + KeyLMenu: "LMenu", + KeyRMenu: "RMenu", + KeyBrowserBack: "BrowserBack", + KeyBrowserForward: "BrowserForward", + KeyBrowserRefresh: "BrowserRefresh", + KeyBrowserStop: "BrowserStop", + KeyBrowserSearch: "BrowserSearch", + KeyBrowserFavorites: "BrowserFavorites", + KeyBrowserHome: "BrowserHome", + KeyVolumeMute: "VolumeMute", + KeyVolumeDown: "VolumeDown", + KeyVolumeUp: "VolumeUp", + KeyMediaNextTrack: "MediaNextTrack", + KeyMediaPrevTrack: "MediaPrevTrack", + KeyMediaStop: "MediaStop", + KeyMediaPlayPause: "MediaPlayPause", + KeyLaunchMail: "LaunchMail", + KeyLaunchMediaSelect: "LaunchMediaSelect", + KeyLaunchApp1: "LaunchApp1", + KeyLaunchApp2: "LaunchApp2", + KeyOEM1: "OEM1", + KeyOEMPlus: "OEMPlus", + KeyOEMComma: "OEMComma", + KeyOEMMinus: "OEMMinus", + KeyOEMPeriod: "OEMPeriod", + KeyOEM2: "OEM2", + KeyOEM3: "OEM3", + KeyOEM4: "OEM4", + KeyOEM5: "OEM5", + KeyOEM6: "OEM6", + KeyOEM7: "OEM7", + KeyOEM8: "OEM8", + KeyOEM102: "OEM102", + KeyProcessKey: "ProcessKey", + KeyPacket: "Packet", + KeyAttn: "Attn", + KeyCRSel: "CRSel", + KeyEXSel: "EXSel", + KeyErEOF: "ErEOF", + KeyPlay: "Play", + KeyZoom: "Zoom", + KeyNoName: "NoName", + KeyPA1: "PA1", + KeyOEMClear: "OEMClear", +} + +type Modifiers byte + +func (m Modifiers) String() string { + return modifiers2string[m] +} + +var modifiers2string = map[Modifiers]string{ + ModShift: "Shift", + ModControl: "Ctrl", + ModControl | ModShift: "Ctrl+Shift", + ModAlt: "Alt", + ModAlt | ModShift: "Alt+Shift", + ModAlt | ModControl | ModShift: "Alt+Ctrl+Shift", +} + +const ( + ModShift Modifiers = 1 << iota + ModControl + ModAlt +) + +func ModifiersDown() Modifiers { + var m Modifiers + + if ShiftDown() { + m |= ModShift + } + if ControlDown() { + m |= ModControl + } + if AltDown() { + m |= ModAlt + } + + return m +} + +type Shortcut struct { + Modifiers Modifiers + Key Key +} + +func (s Shortcut) String() string { + m := s.Modifiers.String() + if m == "" { + return s.Key.String() + } + + b := new(bytes.Buffer) + + b.WriteString(m) + b.WriteRune('+') + b.WriteString(s.Key.String()) + + return b.String() +} + +func AltDown() bool { + return w32.GetKeyState(int32(KeyAlt))>>15 != 0 +} + +func ControlDown() bool { + return w32.GetKeyState(int32(KeyControl))>>15 != 0 +} + +func ShiftDown() bool { + return w32.GetKeyState(int32(KeyShift))>>15 != 0 +} diff --git a/v2/internal/frontend/desktop/windows/winc/label.go b/v2/internal/frontend/desktop/windows/winc/label.go new file mode 100644 index 000000000..b866ea7b6 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/label.go @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + */ + +package winc + +import ( + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type Label struct { + ControlBase +} + +func NewLabel(parent Controller) *Label { + lb := new(Label) + + lb.InitControl("STATIC", parent, 0, w32.WS_CHILD|w32.WS_VISIBLE|w32.SS_LEFTNOWORDWRAP) + RegMsgHandler(lb) + + lb.SetFont(DefaultFont) + lb.SetText("Label") + lb.SetSize(100, 25) + return lb +} + +func (lb *Label) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + return w32.DefWindowProc(lb.hwnd, msg, wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/layout.go b/v2/internal/frontend/desktop/windows/winc/layout.go new file mode 100644 index 000000000..b16f041c3 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/layout.go @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + */ + +package winc + +import ( + "encoding/json" + "fmt" + "io" + "os" + "sort" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +// Dockable component must satisfy interface to be docked. +type Dockable interface { + Handle() w32.HWND + + Pos() (x, y int) + Width() int + Height() int + Visible() bool + + SetPos(x, y int) + SetSize(width, height int) + + OnMouseMove() *EventManager + OnLBUp() *EventManager +} + +// DockAllow is window, panel or other component that satisfies interface. +type DockAllow interface { + Handle() w32.HWND + ClientWidth() int + ClientHeight() int + SetLayout(mng LayoutManager) +} + +// Various layout managers +type Direction int + +const ( + Top Direction = iota + Bottom + Left + Right + Fill +) + +type LayoutControl struct { + child Dockable + dir Direction +} + +type LayoutControls []*LayoutControl + +type SimpleDock struct { + parent DockAllow + layoutCtl LayoutControls + loadedState bool +} + +// DockState gets saved and loaded from json +type CtlState struct { + X, Y, Width, Height int +} + +type LayoutState struct { + WindowState string + Controls []*CtlState +} + +func (lc LayoutControls) Len() int { return len(lc) } +func (lc LayoutControls) Swap(i, j int) { lc[i], lc[j] = lc[j], lc[i] } +func (lc LayoutControls) Less(i, j int) bool { return lc[i].dir < lc[j].dir } + +func NewSimpleDock(parent DockAllow) *SimpleDock { + d := &SimpleDock{parent: parent} + parent.SetLayout(d) + return d +} + +// Layout management for the child controls. +func (sd *SimpleDock) Dock(child Dockable, dir Direction) { + sd.layoutCtl = append(sd.layoutCtl, &LayoutControl{child, dir}) +} + +// SaveState of the layout. Only works for Docks with parent set to main form. +func (sd *SimpleDock) SaveState(w io.Writer) error { + var ls LayoutState + + var wp w32.WINDOWPLACEMENT + wp.Length = uint32(unsafe.Sizeof(wp)) + if !w32.GetWindowPlacement(sd.parent.Handle(), &wp) { + return fmt.Errorf("GetWindowPlacement failed") + } + + ls.WindowState = fmt.Sprint( + wp.Flags, wp.ShowCmd, + wp.PtMinPosition.X, wp.PtMinPosition.Y, + wp.PtMaxPosition.X, wp.PtMaxPosition.Y, + wp.RcNormalPosition.Left, wp.RcNormalPosition.Top, + wp.RcNormalPosition.Right, wp.RcNormalPosition.Bottom) + + for _, c := range sd.layoutCtl { + x, y := c.child.Pos() + w, h := c.child.Width(), c.child.Height() + + ctl := &CtlState{X: x, Y: y, Width: w, Height: h} + ls.Controls = append(ls.Controls, ctl) + } + + if err := json.NewEncoder(w).Encode(ls); err != nil { + return err + } + + return nil +} + +// LoadState of the layout. Only works for Docks with parent set to main form. +func (sd *SimpleDock) LoadState(r io.Reader) error { + var ls LayoutState + + if err := json.NewDecoder(r).Decode(&ls); err != nil { + return err + } + + var wp w32.WINDOWPLACEMENT + if _, err := fmt.Sscan(ls.WindowState, + &wp.Flags, &wp.ShowCmd, + &wp.PtMinPosition.X, &wp.PtMinPosition.Y, + &wp.PtMaxPosition.X, &wp.PtMaxPosition.Y, + &wp.RcNormalPosition.Left, &wp.RcNormalPosition.Top, + &wp.RcNormalPosition.Right, &wp.RcNormalPosition.Bottom); err != nil { + return err + } + wp.Length = uint32(unsafe.Sizeof(wp)) + + if !w32.SetWindowPlacement(sd.parent.Handle(), &wp) { + return fmt.Errorf("SetWindowPlacement failed") + } + + // if number of controls in the saved layout does not match + // current number on screen - something changed and we do not reload + // rest of control sizes from json + if len(sd.layoutCtl) != len(ls.Controls) { + return nil + } + + for i, c := range sd.layoutCtl { + c.child.SetPos(ls.Controls[i].X, ls.Controls[i].Y) + c.child.SetSize(ls.Controls[i].Width, ls.Controls[i].Height) + } + return nil +} + +// SaveStateFile convenience function. +func (sd *SimpleDock) SaveStateFile(file string) error { + f, err := os.Create(file) + if err != nil { + return err + } + return sd.SaveState(f) +} + +// LoadStateFile loads state ignores error if file is not found. +func (sd *SimpleDock) LoadStateFile(file string) error { + f, err := os.Open(file) + if err != nil { + return nil // if file is not found or not accessible ignore it + } + return sd.LoadState(f) +} + +// Update is called to resize child items based on layout directions. +func (sd *SimpleDock) Update() { + sort.Stable(sd.layoutCtl) + + x, y := 0, 0 + w, h := sd.parent.ClientWidth(), sd.parent.ClientHeight() + winw, winh := w, h + + for _, c := range sd.layoutCtl { + // Non visible controls do not preserve space. + if !c.child.Visible() { + continue + } + + switch c.dir { + case Top: + c.child.SetPos(x, y) + c.child.SetSize(w, c.child.Height()) + h -= c.child.Height() + y += c.child.Height() + case Bottom: + c.child.SetPos(x, winh-c.child.Height()) + c.child.SetSize(w, c.child.Height()) + h -= c.child.Height() + winh -= c.child.Height() + case Left: + c.child.SetPos(x, y) + c.child.SetSize(c.child.Width(), h) + w -= c.child.Width() + x += c.child.Width() + case Right: + c.child.SetPos(winw-c.child.Width(), y) + c.child.SetSize(c.child.Width(), h) + w -= c.child.Width() + winw -= c.child.Width() + case Fill: + // fill available space + c.child.SetPos(x, y) + c.child.SetSize(w, h) + } + //c.child.Invalidate(true) + } +} diff --git a/v2/internal/frontend/desktop/windows/winc/listview.go b/v2/internal/frontend/desktop/windows/winc/listview.go new file mode 100644 index 000000000..a138b55f9 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/listview.go @@ -0,0 +1,547 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + */ + +package winc + +import ( + "errors" + "fmt" + "syscall" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +// ListItem represents an item in a ListView widget. +type ListItem interface { + Text() []string // Text returns the text of the multi-column item. + ImageIndex() int // ImageIndex is used only if SetImageList is called on the listview +} + +// ListItemChecker is used for checkbox support in ListView. +type ListItemChecker interface { + Checked() bool + SetChecked(checked bool) +} + +// ListItemSetter is used in OnEndLabelEdit event. +type ListItemSetter interface { + SetText(s string) // set first item in the array via LabelEdit event +} + +// StringListItem is helper for basic string lists. +type StringListItem struct { + ID int + Data string + Check bool +} + +func (s StringListItem) Text() []string { return []string{s.Data} } +func (s StringListItem) Checked() bool { return s.Check } +func (s StringListItem) SetChecked(checked bool) { s.Check = checked } +func (s StringListItem) ImageIndex() int { return 0 } + +type ListView struct { + ControlBase + + iml *ImageList + lastIndex int + cols int // count of columns + + item2Handle map[ListItem]uintptr + handle2Item map[uintptr]ListItem + + onEndLabelEdit EventManager + onDoubleClick EventManager + onClick EventManager + onKeyDown EventManager + onItemChanging EventManager + onItemChanged EventManager + onCheckChanged EventManager + onViewChange EventManager + onEndScroll EventManager +} + +func NewListView(parent Controller) *ListView { + lv := new(ListView) + + lv.InitControl("SysListView32", parent /*w32.WS_EX_CLIENTEDGE*/, 0, + w32.WS_CHILD|w32.WS_VISIBLE|w32.WS_TABSTOP|w32.LVS_REPORT|w32.LVS_EDITLABELS|w32.LVS_SHOWSELALWAYS) + + lv.item2Handle = make(map[ListItem]uintptr) + lv.handle2Item = make(map[uintptr]ListItem) + + RegMsgHandler(lv) + + lv.SetFont(DefaultFont) + lv.SetSize(200, 400) + + if err := lv.SetTheme("Explorer"); err != nil { + // theme error is ignored + } + return lv +} + +// FIXME: Changes the state of an item in a list-view control. Refer LVM_SETITEMSTATE message. +func (lv *ListView) setItemState(i int, state, mask uint) { + var item w32.LVITEM + item.State, item.StateMask = uint32(state), uint32(mask) + w32.SendMessage(lv.hwnd, w32.LVM_SETITEMSTATE, uintptr(i), uintptr(unsafe.Pointer(&item))) +} + +func (lv *ListView) EnableSingleSelect(enable bool) { + SetStyle(lv.hwnd, enable, w32.LVS_SINGLESEL) +} + +func (lv *ListView) EnableSortHeader(enable bool) { + SetStyle(lv.hwnd, enable, w32.LVS_NOSORTHEADER) +} + +func (lv *ListView) EnableSortAscending(enable bool) { + SetStyle(lv.hwnd, enable, w32.LVS_SORTASCENDING) +} + +func (lv *ListView) EnableEditLabels(enable bool) { + SetStyle(lv.hwnd, enable, w32.LVS_EDITLABELS) +} + +func (lv *ListView) EnableFullRowSelect(enable bool) { + if enable { + w32.SendMessage(lv.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, w32.LVS_EX_FULLROWSELECT) + } else { + w32.SendMessage(lv.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, w32.LVS_EX_FULLROWSELECT, 0) + } +} + +func (lv *ListView) EnableDoubleBuffer(enable bool) { + if enable { + w32.SendMessage(lv.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, w32.LVS_EX_DOUBLEBUFFER) + } else { + w32.SendMessage(lv.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, w32.LVS_EX_DOUBLEBUFFER, 0) + } +} + +func (lv *ListView) EnableHotTrack(enable bool) { + if enable { + w32.SendMessage(lv.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, w32.LVS_EX_TRACKSELECT) + } else { + w32.SendMessage(lv.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, w32.LVS_EX_TRACKSELECT, 0) + } +} + +func (lv *ListView) SetItemCount(count int) bool { + return w32.SendMessage(lv.hwnd, w32.LVM_SETITEMCOUNT, uintptr(count), 0) != 0 +} + +func (lv *ListView) ItemCount() int { + return int(w32.SendMessage(lv.hwnd, w32.LVM_GETITEMCOUNT, 0, 0)) +} + +func (lv *ListView) ItemAt(x, y int) ListItem { + hti := w32.LVHITTESTINFO{Pt: w32.POINT{int32(x), int32(y)}} + w32.SendMessage(lv.hwnd, w32.LVM_HITTEST, 0, uintptr(unsafe.Pointer(&hti))) + return lv.findItemByIndex(int(hti.IItem)) +} + +func (lv *ListView) Items() (list []ListItem) { + for item := range lv.item2Handle { + list = append(list, item) + } + return list +} + +func (lv *ListView) AddColumn(caption string, width int) { + var lc w32.LVCOLUMN + lc.Mask = w32.LVCF_TEXT + if width != 0 { + lc.Mask = lc.Mask | w32.LVCF_WIDTH + lc.Cx = int32(width) + } + lc.PszText = syscall.StringToUTF16Ptr(caption) + lv.insertLvColumn(&lc, lv.cols) + lv.cols++ +} + +// StretchLastColumn makes the last column take up all remaining horizontal +// space of the *ListView. +// The effect of this is not persistent. +func (lv *ListView) StretchLastColumn() error { + if lv.cols == 0 { + return nil + } + if w32.SendMessage(lv.hwnd, w32.LVM_SETCOLUMNWIDTH, uintptr(lv.cols-1), w32.LVSCW_AUTOSIZE_USEHEADER) == 0 { + //panic("LVM_SETCOLUMNWIDTH failed") + } + return nil +} + +// CheckBoxes returns if the *TableView has check boxes. +func (lv *ListView) CheckBoxes() bool { + return w32.SendMessage(lv.hwnd, w32.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0)&w32.LVS_EX_CHECKBOXES > 0 +} + +// SetCheckBoxes sets if the *TableView has check boxes. +func (lv *ListView) SetCheckBoxes(value bool) { + exStyle := w32.SendMessage(lv.hwnd, w32.LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0) + oldStyle := exStyle + if value { + exStyle |= w32.LVS_EX_CHECKBOXES + } else { + exStyle &^= w32.LVS_EX_CHECKBOXES + } + if exStyle != oldStyle { + w32.SendMessage(lv.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, exStyle) + } + + mask := w32.SendMessage(lv.hwnd, w32.LVM_GETCALLBACKMASK, 0, 0) + if value { + mask |= w32.LVIS_STATEIMAGEMASK + } else { + mask &^= w32.LVIS_STATEIMAGEMASK + } + + if w32.SendMessage(lv.hwnd, w32.LVM_SETCALLBACKMASK, mask, 0) == w32.FALSE { + panic("SendMessage(LVM_SETCALLBACKMASK)") + } +} + +func (lv *ListView) applyImage(lc *w32.LVITEM, imIndex int) { + if lv.iml != nil { + lc.Mask |= w32.LVIF_IMAGE + lc.IImage = int32(imIndex) + } +} + +func (lv *ListView) AddItem(item ListItem) { + lv.InsertItem(item, lv.ItemCount()) +} + +func (lv *ListView) InsertItem(item ListItem, index int) { + text := item.Text() + li := &w32.LVITEM{ + Mask: w32.LVIF_TEXT | w32.LVIF_PARAM, + PszText: syscall.StringToUTF16Ptr(text[0]), + IItem: int32(index), + } + + lv.lastIndex++ + ix := new(int) + *ix = lv.lastIndex + li.LParam = uintptr(*ix) + lv.handle2Item[li.LParam] = item + lv.item2Handle[item] = li.LParam + + lv.applyImage(li, item.ImageIndex()) + lv.insertLvItem(li) + + for i := 1; i < len(text); i++ { + li.Mask = w32.LVIF_TEXT + li.PszText = syscall.StringToUTF16Ptr(text[i]) + li.ISubItem = int32(i) + lv.setLvItem(li) + } +} + +func (lv *ListView) UpdateItem(item ListItem) bool { + lparam, ok := lv.item2Handle[item] + if !ok { + return false + } + + index := lv.findIndexByItem(item) + if index == -1 { + return false + } + + text := item.Text() + li := &w32.LVITEM{ + Mask: w32.LVIF_TEXT | w32.LVIF_PARAM, + PszText: syscall.StringToUTF16Ptr(text[0]), + LParam: lparam, + IItem: int32(index), + } + + lv.applyImage(li, item.ImageIndex()) + lv.setLvItem(li) + + for i := 1; i < len(text); i++ { + li.Mask = w32.LVIF_TEXT + li.PszText = syscall.StringToUTF16Ptr(text[i]) + li.ISubItem = int32(i) + lv.setLvItem(li) + } + return true +} + +func (lv *ListView) insertLvColumn(lvColumn *w32.LVCOLUMN, iCol int) { + w32.SendMessage(lv.hwnd, w32.LVM_INSERTCOLUMN, uintptr(iCol), uintptr(unsafe.Pointer(lvColumn))) +} + +func (lv *ListView) insertLvItem(lvItem *w32.LVITEM) { + w32.SendMessage(lv.hwnd, w32.LVM_INSERTITEM, 0, uintptr(unsafe.Pointer(lvItem))) +} + +func (lv *ListView) setLvItem(lvItem *w32.LVITEM) { + w32.SendMessage(lv.hwnd, w32.LVM_SETITEM, 0, uintptr(unsafe.Pointer(lvItem))) +} + +func (lv *ListView) DeleteAllItems() bool { + if w32.SendMessage(lv.hwnd, w32.LVM_DELETEALLITEMS, 0, 0) == w32.TRUE { + lv.item2Handle = make(map[ListItem]uintptr) + lv.handle2Item = make(map[uintptr]ListItem) + return true + } + return false +} + +func (lv *ListView) DeleteItem(item ListItem) error { + index := lv.findIndexByItem(item) + if index == -1 { + return errors.New("item not found") + } + + if w32.SendMessage(lv.hwnd, w32.LVM_DELETEITEM, uintptr(index), 0) == 0 { + return errors.New("SendMessage(TVM_DELETEITEM) failed") + } + + h := lv.item2Handle[item] + delete(lv.item2Handle, item) + delete(lv.handle2Item, h) + return nil +} + +func (lv *ListView) findIndexByItem(item ListItem) int { + lparam, ok := lv.item2Handle[item] + if !ok { + return -1 + } + + it := &w32.LVFINDINFO{ + Flags: w32.LVFI_PARAM, + LParam: lparam, + } + var i int = -1 + return int(w32.SendMessage(lv.hwnd, w32.LVM_FINDITEM, uintptr(i), uintptr(unsafe.Pointer(it)))) +} + +func (lv *ListView) findItemByIndex(i int) ListItem { + it := &w32.LVITEM{ + Mask: w32.LVIF_PARAM, + IItem: int32(i), + } + + if w32.SendMessage(lv.hwnd, w32.LVM_GETITEM, 0, uintptr(unsafe.Pointer(it))) == w32.TRUE { + if item, ok := lv.handle2Item[it.LParam]; ok { + return item + } + } + return nil +} + +func (lv *ListView) EnsureVisible(item ListItem) bool { + if i := lv.findIndexByItem(item); i != -1 { + return w32.SendMessage(lv.hwnd, w32.LVM_ENSUREVISIBLE, uintptr(i), 1) == 0 + } + return false +} + +func (lv *ListView) SelectedItem() ListItem { + if items := lv.SelectedItems(); len(items) > 0 { + return items[0] + } + return nil +} + +func (lv *ListView) SetSelectedItem(item ListItem) bool { + if i := lv.findIndexByItem(item); i > -1 { + lv.SetSelectedIndex(i) + return true + } + return false +} + +// mask is used to set the LVITEM.Mask for ListView.GetItem which indicates which attributes you'd like to receive +// of LVITEM. +func (lv *ListView) SelectedItems() []ListItem { + var items []ListItem + + var i int = -1 + for { + if i = int(w32.SendMessage(lv.hwnd, w32.LVM_GETNEXTITEM, uintptr(i), uintptr(w32.LVNI_SELECTED))); i == -1 { + break + } + + if item := lv.findItemByIndex(i); item != nil { + items = append(items, item) + } + } + return items +} + +func (lv *ListView) SelectedCount() uint { + return uint(w32.SendMessage(lv.hwnd, w32.LVM_GETSELECTEDCOUNT, 0, 0)) +} + +// GetSelectedIndex first selected item index. Returns -1 if no item is selected. +func (lv *ListView) SelectedIndex() int { + var i int = -1 + return int(w32.SendMessage(lv.hwnd, w32.LVM_GETNEXTITEM, uintptr(i), uintptr(w32.LVNI_SELECTED))) +} + +// Set i to -1 to select all items. +func (lv *ListView) SetSelectedIndex(i int) { + lv.setItemState(i, w32.LVIS_SELECTED, w32.LVIS_SELECTED) +} + +func (lv *ListView) SetImageList(imageList *ImageList) { + w32.SendMessage(lv.hwnd, w32.LVM_SETIMAGELIST, w32.LVSIL_SMALL, uintptr(imageList.Handle())) + lv.iml = imageList +} + +// Event publishers +func (lv *ListView) OnEndLabelEdit() *EventManager { + return &lv.onEndLabelEdit +} + +func (lv *ListView) OnDoubleClick() *EventManager { + return &lv.onDoubleClick +} + +func (lv *ListView) OnClick() *EventManager { + return &lv.onClick +} + +func (lv *ListView) OnKeyDown() *EventManager { + return &lv.onKeyDown +} + +func (lv *ListView) OnItemChanging() *EventManager { + return &lv.onItemChanging +} + +func (lv *ListView) OnItemChanged() *EventManager { + return &lv.onItemChanged +} + +func (lv *ListView) OnCheckChanged() *EventManager { + return &lv.onCheckChanged +} + +func (lv *ListView) OnViewChange() *EventManager { + return &lv.onViewChange +} + +func (lv *ListView) OnEndScroll() *EventManager { + return &lv.onEndScroll +} + +// Message processer +func (lv *ListView) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + switch msg { + /*case w32.WM_ERASEBKGND: + lv.StretchLastColumn() + println("case w32.WM_ERASEBKGND") + return 1*/ + + case w32.WM_NOTIFY: + nm := (*w32.NMHDR)(unsafe.Pointer(lparam)) + code := int32(nm.Code) + + switch code { + case w32.LVN_BEGINLABELEDITW: + // println("Begin label edit") + case w32.LVN_ENDLABELEDITW: + nmdi := (*w32.NMLVDISPINFO)(unsafe.Pointer(lparam)) + if nmdi.Item.PszText != nil { + fmt.Println(nmdi.Item.PszText, nmdi.Item) + if item, ok := lv.handle2Item[nmdi.Item.LParam]; ok { + lv.onEndLabelEdit.Fire(NewEvent(lv, + &LabelEditEventData{Item: item, + Text: w32.UTF16PtrToString(nmdi.Item.PszText)})) + } + return w32.TRUE + } + case w32.NM_DBLCLK: + lv.onDoubleClick.Fire(NewEvent(lv, nil)) + + case w32.NM_CLICK: + ac := (*w32.NMITEMACTIVATE)(unsafe.Pointer(lparam)) + var hti w32.LVHITTESTINFO + hti.Pt = w32.POINT{ac.PtAction.X, ac.PtAction.Y} + w32.SendMessage(lv.hwnd, w32.LVM_HITTEST, 0, uintptr(unsafe.Pointer(&hti))) + + if hti.Flags == w32.LVHT_ONITEMSTATEICON { + if item := lv.findItemByIndex(int(hti.IItem)); item != nil { + if item, ok := item.(ListItemChecker); ok { + checked := !item.Checked() + item.SetChecked(checked) + lv.onCheckChanged.Fire(NewEvent(lv, item)) + + if w32.SendMessage(lv.hwnd, w32.LVM_UPDATE, uintptr(hti.IItem), 0) == w32.FALSE { + panic("SendMessage(LVM_UPDATE)") + } + } + } + } + + hti.Pt = w32.POINT{ac.PtAction.X, ac.PtAction.Y} + w32.SendMessage(lv.hwnd, w32.LVM_SUBITEMHITTEST, 0, uintptr(unsafe.Pointer(&hti))) + lv.onClick.Fire(NewEvent(lv, hti.ISubItem)) + + case w32.LVN_KEYDOWN: + nmkey := (*w32.NMLVKEYDOWN)(unsafe.Pointer(lparam)) + if nmkey.WVKey == w32.VK_SPACE && lv.CheckBoxes() { + if item := lv.SelectedItem(); item != nil { + if item, ok := item.(ListItemChecker); ok { + checked := !item.Checked() + item.SetChecked(checked) + lv.onCheckChanged.Fire(NewEvent(lv, item)) + } + + index := lv.findIndexByItem(item) + if w32.SendMessage(lv.hwnd, w32.LVM_UPDATE, uintptr(index), 0) == w32.FALSE { + panic("SendMessage(LVM_UPDATE)") + } + } + } + lv.onKeyDown.Fire(NewEvent(lv, nmkey.WVKey)) + key := nmkey.WVKey + w32.SendMessage(lv.Parent().Handle(), w32.WM_KEYDOWN, uintptr(key), 0) + + case w32.LVN_ITEMCHANGING: + // This event also fires when listview has changed via code. + nmlv := (*w32.NMLISTVIEW)(unsafe.Pointer(lparam)) + item := lv.findItemByIndex(int(nmlv.IItem)) + lv.onItemChanging.Fire(NewEvent(lv, item)) + + case w32.LVN_ITEMCHANGED: + // This event also fires when listview has changed via code. + nmlv := (*w32.NMLISTVIEW)(unsafe.Pointer(lparam)) + item := lv.findItemByIndex(int(nmlv.IItem)) + lv.onItemChanged.Fire(NewEvent(lv, item)) + + case w32.LVN_GETDISPINFO: + nmdi := (*w32.NMLVDISPINFO)(unsafe.Pointer(lparam)) + if nmdi.Item.StateMask&w32.LVIS_STATEIMAGEMASK > 0 { + if item, ok := lv.handle2Item[nmdi.Item.LParam]; ok { + if item, ok := item.(ListItemChecker); ok { + + checked := item.Checked() + if checked { + nmdi.Item.State = 0x2000 + } else { + nmdi.Item.State = 0x1000 + } + } + } + } + + lv.onViewChange.Fire(NewEvent(lv, nil)) + + case w32.LVN_ENDSCROLL: + lv.onEndScroll.Fire(NewEvent(lv, nil)) + } + } + return w32.DefWindowProc(lv.hwnd, msg, wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/menu.go b/v2/internal/frontend/desktop/windows/winc/menu.go new file mode 100644 index 000000000..bbfdefade --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/menu.go @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + */ + +package winc + +import ( + "fmt" + "syscall" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +var ( + nextMenuItemID uint16 = 3 + actionsByID = make(map[uint16]*MenuItem) + shortcut2Action = make(map[Shortcut]*MenuItem) + menuItems = make(map[w32.HMENU][]*MenuItem) + radioGroups = make(map[*MenuItem]*RadioGroup) + initialised bool +) + +var NoShortcut = Shortcut{} + +// Menu for main window and context menus on controls. +// Most methods used for both main window menu and context menu. +type Menu struct { + hMenu w32.HMENU + hwnd w32.HWND // hwnd might be nil if it is context menu. +} + +type MenuItem struct { + hMenu w32.HMENU + hSubMenu w32.HMENU // Non zero if this item is in itself a submenu. + + text string + toolTip string + image *Bitmap + shortcut Shortcut + enabled bool + + checkable bool + checked bool + isRadio bool + + id uint16 + + onClick EventManager +} + +type RadioGroup struct { + members []*MenuItem + hwnd w32.HWND +} + +func NewContextMenu() *MenuItem { + hMenu := w32.CreatePopupMenu() + if hMenu == 0 { + panic("failed CreateMenu") + } + + item := &MenuItem{ + hMenu: hMenu, + hSubMenu: hMenu, + } + return item +} + +func (m *Menu) Dispose() { + if m.hMenu != 0 { + w32.DestroyMenu(m.hMenu) + m.hMenu = 0 + } +} + +func (m *Menu) IsDisposed() bool { + return m.hMenu == 0 +} + +func initMenuItemInfoFromAction(mii *w32.MENUITEMINFO, a *MenuItem) { + mii.CbSize = uint32(unsafe.Sizeof(*mii)) + mii.FMask = w32.MIIM_FTYPE | w32.MIIM_ID | w32.MIIM_STATE | w32.MIIM_STRING + if a.image != nil { + mii.FMask |= w32.MIIM_BITMAP + mii.HbmpItem = a.image.handle + } + if a.IsSeparator() { + mii.FType = w32.MFT_SEPARATOR + } else { + mii.FType = w32.MFT_STRING + var text string + if s := a.shortcut; s.Key != 0 { + text = fmt.Sprintf("%s\t%s", a.text, s.String()) + shortcut2Action[a.shortcut] = a + } else { + text = a.text + } + mii.DwTypeData = syscall.StringToUTF16Ptr(text) + mii.Cch = uint32(len([]rune(a.text))) + } + mii.WID = uint32(a.id) + + if a.Enabled() { + mii.FState &^= w32.MFS_DISABLED + } else { + mii.FState |= w32.MFS_DISABLED + } + + if a.Checkable() { + mii.FMask |= w32.MIIM_CHECKMARKS + } + if a.Checked() { + mii.FState |= w32.MFS_CHECKED + } + + if a.hSubMenu != 0 { + mii.FMask |= w32.MIIM_SUBMENU + mii.HSubMenu = a.hSubMenu + } +} + +// Show menu on the main window. +func (m *Menu) Show() { + initialised = true + updateRadioGroups() + if !w32.DrawMenuBar(m.hwnd) { + panic("DrawMenuBar failed") + } +} + +// AddSubMenu returns item that is used as submenu to perform AddItem(s). +func (m *Menu) AddSubMenu(text string) *MenuItem { + hSubMenu := w32.CreateMenu() + if hSubMenu == 0 { + panic("failed CreateMenu") + } + return addMenuItem(m.hMenu, hSubMenu, text, Shortcut{}, nil, false) +} + +// This method will iterate through the menu items, group radio items together, build a +// quick access map and set the initial items +func updateRadioGroups() { + + if !initialised { + return + } + + radioItemsChecked := []*MenuItem{} + radioGroups = make(map[*MenuItem]*RadioGroup) + var currentRadioGroupMembers []*MenuItem + // Iterate the menus + for _, menu := range menuItems { + menuLength := len(menu) + for index, menuItem := range menu { + if menuItem.isRadio { + currentRadioGroupMembers = append(currentRadioGroupMembers, menuItem) + if menuItem.checked { + radioItemsChecked = append(radioItemsChecked, menuItem) + } + + // If end of menu + if index == menuLength-1 { + radioGroup := &RadioGroup{ + members: currentRadioGroupMembers, + hwnd: menuItem.hMenu, + } + // Save the group to each member iin the radiomap + for _, member := range currentRadioGroupMembers { + radioGroups[member] = radioGroup + } + currentRadioGroupMembers = []*MenuItem{} + } + continue + } + + // Not a radio item + if len(currentRadioGroupMembers) > 0 { + radioGroup := &RadioGroup{ + members: currentRadioGroupMembers, + hwnd: menuItem.hMenu, + } + // Save the group to each member iin the radiomap + for _, member := range currentRadioGroupMembers { + radioGroups[member] = radioGroup + } + currentRadioGroupMembers = []*MenuItem{} + } + } + } + + // Enable the checked items + for _, item := range radioItemsChecked { + radioGroup := radioGroups[item] + startID := radioGroup.members[0].id + endID := radioGroup.members[len(radioGroup.members)-1].id + w32.SelectRadioMenuItem(item.id, startID, endID, radioGroup.hwnd) + } + +} + +func (mi *MenuItem) OnClick() *EventManager { + return &mi.onClick +} + +func (mi *MenuItem) AddSeparator() { + addMenuItem(mi.hSubMenu, 0, "-", Shortcut{}, nil, false) +} + +// AddItem adds plain menu item. +func (mi *MenuItem) AddItem(text string, shortcut Shortcut) *MenuItem { + return addMenuItem(mi.hSubMenu, 0, text, shortcut, nil, false) +} + +// AddItemCheckable adds plain menu item that can have a checkmark. +func (mi *MenuItem) AddItemCheckable(text string, shortcut Shortcut) *MenuItem { + return addMenuItem(mi.hSubMenu, 0, text, shortcut, nil, true) +} + +// AddItemRadio adds plain menu item that can have a checkmark and is part of a radio group. +func (mi *MenuItem) AddItemRadio(text string, shortcut Shortcut) *MenuItem { + menuItem := addMenuItem(mi.hSubMenu, 0, text, shortcut, nil, true) + menuItem.isRadio = true + return menuItem +} + +// AddItemWithBitmap adds menu item with shortcut and bitmap. +func (mi *MenuItem) AddItemWithBitmap(text string, shortcut Shortcut, image *Bitmap) *MenuItem { + return addMenuItem(mi.hSubMenu, 0, text, shortcut, image, false) +} + +// AddSubMenu adds a submenu. +func (mi *MenuItem) AddSubMenu(text string) *MenuItem { + hSubMenu := w32.CreatePopupMenu() + if hSubMenu == 0 { + panic("failed CreatePopupMenu") + } + return addMenuItem(mi.hSubMenu, hSubMenu, text, Shortcut{}, nil, false) +} + +// AddItem to the menu, set text to "-" for separators. +func addMenuItem(hMenu, hSubMenu w32.HMENU, text string, shortcut Shortcut, image *Bitmap, checkable bool) *MenuItem { + item := &MenuItem{ + hMenu: hMenu, + hSubMenu: hSubMenu, + text: text, + shortcut: shortcut, + image: image, + enabled: true, + id: nextMenuItemID, + checkable: checkable, + isRadio: false, + //visible: true, + } + nextMenuItemID++ + actionsByID[item.id] = item + menuItems[hMenu] = append(menuItems[hMenu], item) + + var mii w32.MENUITEMINFO + initMenuItemInfoFromAction(&mii, item) + + index := -1 + if !w32.InsertMenuItem(hMenu, uint32(index), true, &mii) { + panic("InsertMenuItem failed") + } + return item +} + +func indexInObserver(a *MenuItem) int { + var idx int + for _, mi := range menuItems[a.hMenu] { + if mi == a { + return idx + } + idx++ + } + return -1 +} + +func findMenuItemByID(id int) *MenuItem { + return actionsByID[uint16(id)] +} + +func (mi *MenuItem) update() { + var mii w32.MENUITEMINFO + initMenuItemInfoFromAction(&mii, mi) + + if !w32.SetMenuItemInfo(mi.hMenu, uint32(indexInObserver(mi)), true, &mii) { + panic("SetMenuItemInfo failed") + } + if mi.isRadio { + mi.updateRadioGroup() + } +} + +func (mi *MenuItem) IsSeparator() bool { return mi.text == "-" } +func (mi *MenuItem) SetSeparator() { mi.text = "-" } + +func (mi *MenuItem) Enabled() bool { return mi.enabled } +func (mi *MenuItem) SetEnabled(b bool) { mi.enabled = b; mi.update() } + +func (mi *MenuItem) Checkable() bool { return mi.checkable } +func (mi *MenuItem) SetCheckable(b bool) { mi.checkable = b; mi.update() } + +func (mi *MenuItem) Checked() bool { return mi.checked } +func (mi *MenuItem) SetChecked(b bool) { + if mi.isRadio { + radioGroup := radioGroups[mi] + if radioGroup != nil { + for _, member := range radioGroup.members { + member.checked = false + } + } + + } + mi.checked = b + mi.update() +} + +func (mi *MenuItem) Text() string { return mi.text } +func (mi *MenuItem) SetText(s string) { mi.text = s; mi.update() } + +func (mi *MenuItem) Image() *Bitmap { return mi.image } +func (mi *MenuItem) SetImage(b *Bitmap) { mi.image = b; mi.update() } + +func (mi *MenuItem) ToolTip() string { return mi.toolTip } +func (mi *MenuItem) SetToolTip(s string) { mi.toolTip = s; mi.update() } + +func (mi *MenuItem) updateRadioGroup() { + radioGroup := radioGroups[mi] + if radioGroup == nil { + return + } + startID := radioGroup.members[0].id + endID := radioGroup.members[len(radioGroup.members)-1].id + w32.SelectRadioMenuItem(mi.id, startID, endID, radioGroup.hwnd) +} diff --git a/v2/internal/frontend/desktop/windows/winc/mousecontrol.go b/v2/internal/frontend/desktop/windows/winc/mousecontrol.go new file mode 100644 index 000000000..12cf634c1 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/mousecontrol.go @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + */ + +package winc + +import ( + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +// MouseControl used for creating custom controls that need mouse hover or mouse leave events. +type MouseControl struct { + ControlBase + isMouseLeft bool +} + +func (cc *MouseControl) Init(parent Controller, className string, exStyle, style uint) { + RegClassOnlyOnce(className) + cc.hwnd = CreateWindow(className, parent, exStyle, style) + cc.parent = parent + RegMsgHandler(cc) + + cc.isMouseLeft = true + cc.SetFont(DefaultFont) +} + +func (cc *MouseControl) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + sender := GetMsgHandler(cc.hwnd) + switch msg { + case w32.WM_CREATE: + internalTrackMouseEvent(cc.hwnd) + cc.onCreate.Fire(NewEvent(sender, nil)) + case w32.WM_CLOSE: + cc.onClose.Fire(NewEvent(sender, nil)) + case w32.WM_MOUSEMOVE: + //if cc.isMouseLeft { + + cc.onMouseHover.Fire(NewEvent(sender, nil)) + //internalTrackMouseEvent(cc.hwnd) + cc.isMouseLeft = false + + //} + case w32.WM_MOUSELEAVE: + cc.onMouseLeave.Fire(NewEvent(sender, nil)) + cc.isMouseLeft = true + } + return w32.DefWindowProc(cc.hwnd, msg, wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/msghandlerregistry.go b/v2/internal/frontend/desktop/windows/winc/msghandlerregistry.go new file mode 100644 index 000000000..a7b61631b --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/msghandlerregistry.go @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +func RegMsgHandler(controller Controller) { + gControllerRegistry[controller.Handle()] = controller +} + +func UnRegMsgHandler(hwnd w32.HWND) { + delete(gControllerRegistry, hwnd) +} + +func GetMsgHandler(hwnd w32.HWND) Controller { + if controller, isExists := gControllerRegistry[hwnd]; isExists { + return controller + } + + return nil +} diff --git a/v2/internal/frontend/desktop/windows/winc/panel.go b/v2/internal/frontend/desktop/windows/winc/panel.go new file mode 100644 index 000000000..4bdf92b9b --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/panel.go @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + */ + +package winc + +import ( + "fmt" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type Panel struct { + ControlBase + layoutMng LayoutManager +} + +func NewPanel(parent Controller) *Panel { + pa := new(Panel) + + RegClassOnlyOnce("winc_Panel") + pa.hwnd = CreateWindow("winc_Panel", parent, w32.WS_EX_CONTROLPARENT, w32.WS_CHILD|w32.WS_VISIBLE) + pa.parent = parent + RegMsgHandler(pa) + + pa.SetFont(DefaultFont) + pa.SetText("") + pa.SetSize(200, 65) + return pa +} + +// SetLayout panel implements DockAllow interface. +func (pa *Panel) SetLayout(mng LayoutManager) { + pa.layoutMng = mng +} + +func (pa *Panel) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + switch msg { + case w32.WM_SIZE, w32.WM_PAINT: + if pa.layoutMng != nil { + pa.layoutMng.Update() + } + } + return w32.DefWindowProc(pa.hwnd, msg, wparam, lparam) +} + +var errorPanelPen = NewPen(w32.PS_GEOMETRIC, 2, NewSolidColorBrush(RGB(255, 128, 128))) +var errorPanelOkPen = NewPen(w32.PS_GEOMETRIC, 2, NewSolidColorBrush(RGB(220, 220, 220))) + +// ErrorPanel shows errors or important messages. +// It is meant to stand out of other on screen controls. +type ErrorPanel struct { + ControlBase + pen *Pen + margin int +} + +// NewErrorPanel. +func NewErrorPanel(parent Controller) *ErrorPanel { + f := new(ErrorPanel) + f.init(parent) + + f.SetFont(DefaultFont) + f.SetText("No errors") + f.SetSize(200, 65) + f.margin = 5 + f.pen = errorPanelOkPen + return f +} + +func (epa *ErrorPanel) init(parent Controller) { + RegClassOnlyOnce("winc_ErrorPanel") + + epa.hwnd = CreateWindow("winc_ErrorPanel", parent, w32.WS_EX_CONTROLPARENT, w32.WS_CHILD|w32.WS_VISIBLE) + epa.parent = parent + + RegMsgHandler(epa) +} + +func (epa *ErrorPanel) SetMargin(margin int) { + epa.margin = margin +} + +func (epa *ErrorPanel) Printf(format string, v ...interface{}) { + epa.SetText(fmt.Sprintf(format, v...)) + epa.ShowAsError(false) +} + +func (epa *ErrorPanel) Errorf(format string, v ...interface{}) { + epa.SetText(fmt.Sprintf(format, v...)) + epa.ShowAsError(true) +} + +func (epa *ErrorPanel) ShowAsError(show bool) { + if show { + epa.pen = errorPanelPen + } else { + epa.pen = errorPanelOkPen + } + epa.Invalidate(true) +} + +func (epa *ErrorPanel) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + switch msg { + case w32.WM_ERASEBKGND: + canvas := NewCanvasFromHDC(w32.HDC(wparam)) + r := epa.Bounds() + r.rect.Left += int32(epa.margin) + r.rect.Right -= int32(epa.margin) + r.rect.Top += int32(epa.margin) + r.rect.Bottom -= int32(epa.margin) + // old code used NewSystemColorBrush(w32.COLOR_BTNFACE) + canvas.DrawFillRect(r, epa.pen, NewSystemColorBrush(w32.COLOR_WINDOW)) + + r.rect.Left += 5 + canvas.DrawText(epa.Text(), r, 0, epa.Font(), RGB(0, 0, 0)) + canvas.Dispose() + return 1 + } + return w32.DefWindowProc(epa.hwnd, msg, wparam, lparam) +} + +// MultiPanel contains other panels and only makes one of them visible. +type MultiPanel struct { + ControlBase + current int + panels []*Panel +} + +func NewMultiPanel(parent Controller) *MultiPanel { + mpa := new(MultiPanel) + + RegClassOnlyOnce("winc_MultiPanel") + mpa.hwnd = CreateWindow("winc_MultiPanel", parent, w32.WS_EX_CONTROLPARENT, w32.WS_CHILD|w32.WS_VISIBLE) + mpa.parent = parent + RegMsgHandler(mpa) + + mpa.SetFont(DefaultFont) + mpa.SetText("") + mpa.SetSize(300, 200) + mpa.current = -1 + return mpa +} + +func (mpa *MultiPanel) Count() int { return len(mpa.panels) } + +// AddPanel adds panels to the internal list, first panel is visible all others are hidden. +func (mpa *MultiPanel) AddPanel(panel *Panel) { + if len(mpa.panels) > 0 { + panel.Hide() + } + mpa.current = 0 + mpa.panels = append(mpa.panels, panel) +} + +// ReplacePanel replaces panel, useful for refreshing controls on screen. +func (mpa *MultiPanel) ReplacePanel(index int, panel *Panel) { + mpa.panels[index] = panel +} + +// DeletePanel removed panel. +func (mpa *MultiPanel) DeletePanel(index int) { + mpa.panels = append(mpa.panels[:index], mpa.panels[index+1:]...) +} + +func (mpa *MultiPanel) Current() int { + return mpa.current +} + +func (mpa *MultiPanel) SetCurrent(index int) { + if index >= len(mpa.panels) { + panic("index greater than number of panels") + } + if mpa.current == -1 { + panic("no current panel, add panels first") + } + for i := range mpa.panels { + if i != index { + mpa.panels[i].Hide() + mpa.panels[i].Invalidate(true) + } + } + mpa.panels[index].Show() + mpa.panels[index].Invalidate(true) + mpa.current = index +} + +func (mpa *MultiPanel) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + switch msg { + case w32.WM_SIZE: + // resize contained panels + for _, p := range mpa.panels { + p.SetPos(0, 0) + p.SetSize(mpa.Size()) + } + } + return w32.DefWindowProc(mpa.hwnd, msg, wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/path.go b/v2/internal/frontend/desktop/windows/winc/path.go new file mode 100644 index 000000000..99f1829e7 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/path.go @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "fmt" + "os" + "path/filepath" + "syscall" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +func knownFolderPath(id w32.CSIDL) (string, error) { + var buf [w32.MAX_PATH]uint16 + + if !w32.SHGetSpecialFolderPath(0, &buf[0], id, false) { + return "", fmt.Errorf("SHGetSpecialFolderPath failed") + } + + return syscall.UTF16ToString(buf[0:]), nil +} + +func AppDataPath() (string, error) { + return knownFolderPath(w32.CSIDL_APPDATA) +} + +func CommonAppDataPath() (string, error) { + return knownFolderPath(w32.CSIDL_COMMON_APPDATA) +} + +func LocalAppDataPath() (string, error) { + return knownFolderPath(w32.CSIDL_LOCAL_APPDATA) +} + +// EnsureAppDataPath uses AppDataPath to ensure storage for local settings and databases. +func EnsureAppDataPath(company, product string) (string, error) { + path, err := AppDataPath() + if err != nil { + return path, err + } + p := filepath.Join(path, company, product) + + if _, err := os.Stat(p); os.IsNotExist(err) { + // path/to/whatever does not exist + if err := os.MkdirAll(p, os.ModePerm); err != nil { + return p, err + } + } + return p, nil +} + +func DriveNames() ([]string, error) { + bufLen := w32.GetLogicalDriveStrings(0, nil) + if bufLen == 0 { + return nil, fmt.Errorf("GetLogicalDriveStrings failed") + } + buf := make([]uint16, bufLen+1) + + bufLen = w32.GetLogicalDriveStrings(bufLen+1, &buf[0]) + if bufLen == 0 { + return nil, fmt.Errorf("GetLogicalDriveStrings failed") + } + + var names []string + for i := 0; i < len(buf)-2; { + name := syscall.UTF16ToString(buf[i:]) + names = append(names, name) + i += len(name) + 1 + } + return names, nil +} diff --git a/v2/internal/frontend/desktop/windows/winc/pen.go b/v2/internal/frontend/desktop/windows/winc/pen.go new file mode 100644 index 000000000..c8091186a --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/pen.go @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type Pen struct { + hPen w32.HPEN + style uint + brush *Brush +} + +func NewPen(style uint, width uint, brush *Brush) *Pen { + if brush == nil { + panic("Brush cannot be nil") + } + + hPen := w32.ExtCreatePen(style, width, brush.GetLOGBRUSH(), 0, nil) + if hPen == 0 { + panic("Failed to create pen") + } + + return &Pen{hPen, style, brush} +} + +func NewNullPen() *Pen { + lb := w32.LOGBRUSH{LbStyle: w32.BS_NULL} + + hPen := w32.ExtCreatePen(w32.PS_COSMETIC|w32.PS_NULL, 1, &lb, 0, nil) + if hPen == 0 { + panic("failed to create null brush") + } + + return &Pen{hPen: hPen} +} + +func (pen *Pen) Style() uint { + return pen.style +} + +func (pen *Pen) Brush() *Brush { + return pen.brush +} + +func (pen *Pen) GetHPEN() w32.HPEN { + return pen.hPen +} + +func (pen *Pen) Dispose() { + if pen.hPen != 0 { + w32.DeleteObject(w32.HGDIOBJ(pen.hPen)) + pen.hPen = 0 + } +} diff --git a/v2/internal/frontend/desktop/windows/winc/progressbar.go b/v2/internal/frontend/desktop/windows/winc/progressbar.go new file mode 100644 index 000000000..19f3c51f4 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/progressbar.go @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + */ + +package winc + +import ( + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type ProgressBar struct { + ControlBase +} + +func NewProgressBar(parent Controller) *ProgressBar { + pb := new(ProgressBar) + + pb.InitControl(w32.PROGRESS_CLASS, parent, 0, w32.WS_CHILD|w32.WS_VISIBLE) + RegMsgHandler(pb) + + pb.SetSize(200, 22) + return pb +} + +func (pr *ProgressBar) Value() int { + ret := w32.SendMessage(pr.hwnd, w32.PBM_GETPOS, 0, 0) + return int(ret) +} + +func (pr *ProgressBar) SetValue(v int) { + w32.SendMessage(pr.hwnd, w32.PBM_SETPOS, uintptr(v), 0) +} + +func (pr *ProgressBar) Range() (min, max uint) { + min = uint(w32.SendMessage(pr.hwnd, w32.PBM_GETRANGE, uintptr(w32.BoolToBOOL(true)), 0)) + max = uint(w32.SendMessage(pr.hwnd, w32.PBM_GETRANGE, uintptr(w32.BoolToBOOL(false)), 0)) + return +} + +func (pr *ProgressBar) SetRange(min, max int) { + w32.SendMessage(pr.hwnd, w32.PBM_SETRANGE32, uintptr(min), uintptr(max)) +} + +func (pr *ProgressBar) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + return w32.DefWindowProc(pr.hwnd, msg, wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/rect.go b/v2/internal/frontend/desktop/windows/winc/rect.go new file mode 100644 index 000000000..43daa7537 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/rect.go @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type Rect struct { + rect w32.RECT +} + +func NewEmptyRect() *Rect { + var newRect Rect + w32.SetRectEmpty(&newRect.rect) + + return &newRect +} + +func NewRect(left, top, right, bottom int) *Rect { + var newRect Rect + w32.SetRectEmpty(&newRect.rect) + newRect.Set(left, top, right, bottom) + + return &newRect +} + +func (re *Rect) Data() (left, top, right, bottom int32) { + left = re.rect.Left + top = re.rect.Top + right = re.rect.Right + bottom = re.rect.Bottom + return +} + +func (re *Rect) Width() int { + return int(re.rect.Right - re.rect.Left) +} + +func (re *Rect) Height() int { + return int(re.rect.Bottom - re.rect.Top) +} + +func (re *Rect) GetW32Rect() *w32.RECT { + return &re.rect +} + +func (re *Rect) Set(left, top, right, bottom int) { + w32.SetRect(&re.rect, left, top, right, bottom) +} + +func (re *Rect) IsEqual(rect *Rect) bool { + return w32.EqualRect(&re.rect, &rect.rect) +} + +func (re *Rect) Inflate(x, y int) { + w32.InflateRect(&re.rect, x, y) +} + +func (re *Rect) Intersect(src *Rect) { + w32.IntersectRect(&re.rect, &re.rect, &src.rect) +} + +func (re *Rect) IsEmpty() bool { + return w32.IsRectEmpty(&re.rect) +} + +func (re *Rect) Offset(x, y int) { + w32.OffsetRect(&re.rect, x, y) +} + +func (re *Rect) IsPointIn(x, y int) bool { + return w32.PtInRect(&re.rect, x, y) +} + +func (re *Rect) Substract(src *Rect) { + w32.SubtractRect(&re.rect, &re.rect, &src.rect) +} + +func (re *Rect) Union(src *Rect) { + w32.UnionRect(&re.rect, &re.rect, &src.rect) +} diff --git a/v2/internal/frontend/desktop/windows/winc/resizer.go b/v2/internal/frontend/desktop/windows/winc/resizer.go new file mode 100644 index 000000000..9c0ac1f78 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/resizer.go @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + */ + +package winc + +import ( + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type VResizer struct { + ControlBase + + control1 Dockable + control2 Dockable + dir Direction + + mouseLeft bool + drag bool +} + +func NewVResizer(parent Controller) *VResizer { + sp := new(VResizer) + + RegClassOnlyOnce("winc_VResizer") + sp.hwnd = CreateWindow("winc_VResizer", parent, w32.WS_EX_CONTROLPARENT, w32.WS_CHILD|w32.WS_VISIBLE) + sp.parent = parent + sp.mouseLeft = true + RegMsgHandler(sp) + + sp.SetFont(DefaultFont) + sp.SetText("") + sp.SetSize(20, 100) + return sp +} + +func (sp *VResizer) SetControl(control1, control2 Dockable, dir Direction, minSize int) { + sp.control1 = control1 + sp.control2 = control2 + if dir != Left && dir != Right { + panic("invalid direction") + } + sp.dir = dir + + // TODO(vi): ADDED + /*internalTrackMouseEvent(control1.Handle()) + internalTrackMouseEvent(control2.Handle()) + + control1.OnMouseMove().Bind(func(e *Event) { + if sp.drag { + x := e.Data.(*MouseEventData).X + sp.update(x) + w32.SetCursor(w32.LoadCursor(0, w32.MakeIntResource(w32.IDC_SIZEWE))) + + } + fmt.Println("control1.OnMouseMove") + }) + + control2.OnMouseMove().Bind(func(e *Event) { + if sp.drag { + x := e.Data.(*MouseEventData).X + sp.update(x) + w32.SetCursor(w32.LoadCursor(0, w32.MakeIntResource(w32.IDC_SIZEWE))) + + } + fmt.Println("control2.OnMouseMove") + }) + + control1.OnLBUp().Bind(func(e *Event) { + sp.drag = false + sp.mouseLeft = true + fmt.Println("control1.OnLBUp") + }) + + control2.OnLBUp().Bind(func(e *Event) { + sp.drag = false + sp.mouseLeft = true + fmt.Println("control2.OnLBUp") + })*/ + + // ---- finish ADDED + +} + +func (sp *VResizer) update(x int) { + pos := x - 10 + + w1, h1 := sp.control1.Width(), sp.control1.Height() + if sp.dir == Left { + w1 += pos + } else { + w1 -= pos + } + sp.control1.SetSize(w1, h1) + fm := sp.parent.(*Form) + fm.UpdateLayout() + + w32.SetCursor(w32.LoadCursor(0, w32.MakeIntResource(w32.IDC_ARROW))) +} + +func (sp *VResizer) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + switch msg { + case w32.WM_CREATE: + internalTrackMouseEvent(sp.hwnd) + + case w32.WM_MOUSEMOVE: + if sp.drag { + x, _ := genPoint(lparam) + sp.update(x) + } else { + w32.SetCursor(w32.LoadCursor(0, w32.MakeIntResource(w32.IDC_SIZEWE))) + } + + if sp.mouseLeft { + internalTrackMouseEvent(sp.hwnd) + sp.mouseLeft = false + } + + case w32.WM_MOUSELEAVE: + sp.drag = false + sp.mouseLeft = true + + case w32.WM_LBUTTONUP: + sp.drag = false + + case w32.WM_LBUTTONDOWN: + sp.drag = true + } + return w32.DefWindowProc(sp.hwnd, msg, wparam, lparam) +} + +type HResizer struct { + ControlBase + + control1 Dockable + control2 Dockable + dir Direction + mouseLeft bool + drag bool +} + +func NewHResizer(parent Controller) *HResizer { + sp := new(HResizer) + + RegClassOnlyOnce("winc_HResizer") + sp.hwnd = CreateWindow("winc_HResizer", parent, w32.WS_EX_CONTROLPARENT, w32.WS_CHILD|w32.WS_VISIBLE) + sp.parent = parent + sp.mouseLeft = true + RegMsgHandler(sp) + + sp.SetFont(DefaultFont) + sp.SetText("") + sp.SetSize(100, 20) + + return sp +} + +func (sp *HResizer) SetControl(control1, control2 Dockable, dir Direction, minSize int) { + sp.control1 = control1 + sp.control2 = control2 + if dir != Top && dir != Bottom { + panic("invalid direction") + } + sp.dir = dir + +} + +func (sp *HResizer) update(y int) { + pos := y - 10 + + w1, h1 := sp.control1.Width(), sp.control1.Height() + if sp.dir == Top { + h1 += pos + } else { + h1 -= pos + } + sp.control1.SetSize(w1, h1) + + fm := sp.parent.(*Form) + fm.UpdateLayout() + + w32.SetCursor(w32.LoadCursor(0, w32.MakeIntResource(w32.IDC_ARROW))) +} + +func (sp *HResizer) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + switch msg { + case w32.WM_CREATE: + internalTrackMouseEvent(sp.hwnd) + + case w32.WM_MOUSEMOVE: + if sp.drag { + _, y := genPoint(lparam) + sp.update(y) + } else { + w32.SetCursor(w32.LoadCursor(0, w32.MakeIntResource(w32.IDC_SIZENS))) + } + + if sp.mouseLeft { + internalTrackMouseEvent(sp.hwnd) + sp.mouseLeft = false + } + + case w32.WM_MOUSELEAVE: + sp.drag = false + sp.mouseLeft = true + + case w32.WM_LBUTTONUP: + sp.drag = false + + case w32.WM_LBUTTONDOWN: + sp.drag = true + } + return w32.DefWindowProc(sp.hwnd, msg, wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/scrollview.go b/v2/internal/frontend/desktop/windows/winc/scrollview.go new file mode 100644 index 000000000..395dfed68 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/scrollview.go @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + */ + +package winc + +import ( + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type ScrollView struct { + ControlBase + child Dockable +} + +func NewScrollView(parent Controller) *ScrollView { + sv := new(ScrollView) + + RegClassOnlyOnce("winc_ScrollView") + sv.hwnd = CreateWindow("winc_ScrollView", parent, w32.WS_EX_CONTROLPARENT, + w32.WS_CHILD|w32.WS_HSCROLL|w32.WS_VISIBLE|w32.WS_VSCROLL) + sv.parent = parent + RegMsgHandler(sv) + + sv.SetFont(DefaultFont) + sv.SetText("") + sv.SetSize(200, 200) + return sv +} + +func (sv *ScrollView) SetChild(child Dockable) { + sv.child = child +} + +func (sv *ScrollView) UpdateScrollBars() { + w, h := sv.child.Width(), sv.child.Height() + sw, sh := sv.Size() + + var si w32.SCROLLINFO + si.CbSize = uint32(unsafe.Sizeof(si)) + si.FMask = w32.SIF_PAGE | w32.SIF_RANGE + + si.NMax = int32(w - 1) + si.NPage = uint32(sw) + w32.SetScrollInfo(sv.hwnd, w32.SB_HORZ, &si, true) + x := sv.scroll(w32.SB_HORZ, w32.SB_THUMBPOSITION) + + si.NMax = int32(h) + si.NPage = uint32(sh) + w32.SetScrollInfo(sv.hwnd, w32.SB_VERT, &si, true) + y := sv.scroll(w32.SB_VERT, w32.SB_THUMBPOSITION) + + sv.child.SetPos(x, y) +} + +func (sv *ScrollView) scroll(sb int32, cmd uint16) int { + var pos int32 + var si w32.SCROLLINFO + si.CbSize = uint32(unsafe.Sizeof(si)) + si.FMask = w32.SIF_PAGE | w32.SIF_POS | w32.SIF_RANGE | w32.SIF_TRACKPOS + + w32.GetScrollInfo(sv.hwnd, sb, &si) + pos = si.NPos + + switch cmd { + case w32.SB_LINELEFT: // == win.SB_LINEUP + pos -= 20 + + case w32.SB_LINERIGHT: // == win.SB_LINEDOWN + pos += 20 + + case w32.SB_PAGELEFT: // == win.SB_PAGEUP + pos -= int32(si.NPage) + + case w32.SB_PAGERIGHT: // == win.SB_PAGEDOWN + pos += int32(si.NPage) + + case w32.SB_THUMBTRACK: + pos = si.NTrackPos + } + + if pos < 0 { + pos = 0 + } + if pos > si.NMax+1-int32(si.NPage) { + pos = si.NMax + 1 - int32(si.NPage) + } + + si.FMask = w32.SIF_POS + si.NPos = pos + w32.SetScrollInfo(sv.hwnd, sb, &si, true) + + return -int(pos) +} + +func (sv *ScrollView) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + if sv.child != nil { + switch msg { + case w32.WM_PAINT: + sv.UpdateScrollBars() + + case w32.WM_HSCROLL: + x, y := sv.child.Pos() + x = sv.scroll(w32.SB_HORZ, w32.LOWORD(uint32(wparam))) + sv.child.SetPos(x, y) + + case w32.WM_VSCROLL: + x, y := sv.child.Pos() + y = sv.scroll(w32.SB_VERT, w32.LOWORD(uint32(wparam))) + sv.child.SetPos(x, y) + + case w32.WM_SIZE, w32.WM_SIZING: + sv.UpdateScrollBars() + } + } + return w32.DefWindowProc(sv.hwnd, msg, wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/slider.go b/v2/internal/frontend/desktop/windows/winc/slider.go new file mode 100644 index 000000000..2a1e55030 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/slider.go @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + */ + +package winc + +import "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" + +type Slider struct { + ControlBase + prevPos int + + onScroll EventManager +} + +func NewSlider(parent Controller) *Slider { + tb := new(Slider) + + tb.InitControl("msctls_trackbar32", parent, 0, w32.WS_TABSTOP|w32.WS_VISIBLE|w32.WS_CHILD /*|w32.TBS_AUTOTICKS*/) + RegMsgHandler(tb) + + tb.SetFont(DefaultFont) + tb.SetText("Slider") + tb.SetSize(200, 32) + + tb.SetRange(0, 100) + tb.SetPage(10) + return tb +} + +func (tb *Slider) OnScroll() *EventManager { + return &tb.onScroll +} + +func (tb *Slider) Value() int { + ret := w32.SendMessage(tb.hwnd, w32.TBM_GETPOS, 0, 0) + return int(ret) +} + +func (tb *Slider) SetValue(v int) { + tb.prevPos = v + w32.SendMessage(tb.hwnd, w32.TBM_SETPOS, uintptr(w32.BoolToBOOL(true)), uintptr(v)) +} + +func (tb *Slider) Range() (min, max int) { + min = int(w32.SendMessage(tb.hwnd, w32.TBM_GETRANGEMIN, 0, 0)) + max = int(w32.SendMessage(tb.hwnd, w32.TBM_GETRANGEMAX, 0, 0)) + return min, max +} + +func (tb *Slider) SetRange(min, max int) { + w32.SendMessage(tb.hwnd, w32.TBM_SETRANGE, uintptr(w32.BoolToBOOL(true)), uintptr(w32.MAKELONG(uint16(min), uint16(max)))) +} + +func (tb *Slider) SetPage(pagesize int) { + w32.SendMessage(tb.hwnd, w32.TBM_SETPAGESIZE, 0, uintptr(pagesize)) +} + +func (tb *Slider) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + /* + // REMOVE: + // following code did not work, used workaround below + code := w32.LOWORD(uint32(wparam)) + + switch code { + case w32.TB_ENDTRACK: + tb.onScroll.Fire(NewEvent(tb, nil)) + }*/ + + newPos := tb.Value() + if newPos != tb.prevPos { + tb.onScroll.Fire(NewEvent(tb, nil)) + tb.prevPos = newPos + } + + return w32.DefWindowProc(tb.hwnd, msg, wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/tabview.go b/v2/internal/frontend/desktop/windows/winc/tabview.go new file mode 100644 index 000000000..161877119 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/tabview.go @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + */ + +package winc + +import ( + "syscall" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +// TabView creates MultiPanel internally and manages tabs as panels. +type TabView struct { + ControlBase + + panels *MultiPanel + onSelectedChange EventManager +} + +func NewTabView(parent Controller) *TabView { + tv := new(TabView) + + tv.InitControl("SysTabControl32", parent, 0, + w32.WS_CHILD|w32.WS_VISIBLE|w32.WS_TABSTOP|w32.WS_CLIPSIBLINGS) + RegMsgHandler(tv) + + tv.panels = NewMultiPanel(parent) + + tv.SetFont(DefaultFont) + tv.SetSize(200, 24) + return tv +} + +func (tv *TabView) Panels() *MultiPanel { + return tv.panels +} + +func (tv *TabView) tcitemFromPage(panel *Panel) *w32.TCITEM { + text := syscall.StringToUTF16(panel.Text()) + item := &w32.TCITEM{ + Mask: w32.TCIF_TEXT, + PszText: &text[0], + CchTextMax: int32(len(text)), + } + return item +} + +func (tv *TabView) AddPanel(text string) *Panel { + panel := NewPanel(tv.panels) + panel.SetText(text) + + item := tv.tcitemFromPage(panel) + index := tv.panels.Count() + idx := int(w32.SendMessage(tv.hwnd, w32.TCM_INSERTITEM, uintptr(index), uintptr(unsafe.Pointer(item)))) + if idx == -1 { + panic("SendMessage(TCM_INSERTITEM) failed") + } + + tv.panels.AddPanel(panel) + tv.SetCurrent(idx) + return panel +} + +func (tv *TabView) DeletePanel(index int) { + w32.SendMessage(tv.hwnd, w32.TCM_DELETEITEM, uintptr(index), 0) + tv.panels.DeletePanel(index) + switch { + case tv.panels.Count() > index: + tv.SetCurrent(index) + case tv.panels.Count() == 0: + tv.SetCurrent(0) + } +} + +func (tv *TabView) Current() int { + return tv.panels.Current() +} + +func (tv *TabView) SetCurrent(index int) { + if index < 0 || index >= tv.panels.Count() { + panic("invalid index") + } + if ret := int(w32.SendMessage(tv.hwnd, w32.TCM_SETCURSEL, uintptr(index), 0)); ret == -1 { + panic("SendMessage(TCM_SETCURSEL) failed") + } + tv.panels.SetCurrent(index) +} + +func (tv *TabView) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + switch msg { + case w32.WM_NOTIFY: + nmhdr := (*w32.NMHDR)(unsafe.Pointer(lparam)) + + switch int32(nmhdr.Code) { + case w32.TCN_SELCHANGE: + cur := int(w32.SendMessage(tv.hwnd, w32.TCM_GETCURSEL, 0, 0)) + tv.SetCurrent(cur) + + tv.onSelectedChange.Fire(NewEvent(tv, nil)) + } + } + return w32.DefWindowProc(tv.hwnd, msg, wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/toolbar.go b/v2/internal/frontend/desktop/windows/winc/toolbar.go new file mode 100644 index 000000000..ae1fe393d --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/toolbar.go @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + */ + +package winc + +import ( + "syscall" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type Toolbar struct { + ControlBase + iml *ImageList + + buttons []*ToolButton +} + +type ToolButton struct { + tb *Toolbar + + text string + enabled bool + checkable bool + checked bool + image int + + onClick EventManager +} + +func (bt *ToolButton) OnClick() *EventManager { + return &bt.onClick +} + +func (bt *ToolButton) update() { bt.tb.update(bt) } + +func (bt *ToolButton) IsSeparator() bool { return bt.text == "-" } +func (bt *ToolButton) SetSeparator() { bt.text = "-" } + +func (bt *ToolButton) Enabled() bool { return bt.enabled } +func (bt *ToolButton) SetEnabled(b bool) { bt.enabled = b; bt.update() } + +func (bt *ToolButton) Checkable() bool { return bt.checkable } +func (bt *ToolButton) SetCheckable(b bool) { bt.checkable = b; bt.update() } + +func (bt *ToolButton) Checked() bool { return bt.checked } +func (bt *ToolButton) SetChecked(b bool) { bt.checked = b; bt.update() } + +func (bt *ToolButton) Text() string { return bt.text } +func (bt *ToolButton) SetText(s string) { bt.text = s; bt.update() } + +func (bt *ToolButton) Image() int { return bt.image } +func (bt *ToolButton) SetImage(i int) { bt.image = i; bt.update() } + +// NewHToolbar creates horizontal toolbar with text on same line as image. +func NewHToolbar(parent Controller) *Toolbar { + return newToolbar(parent, w32.CCS_NODIVIDER|w32.TBSTYLE_FLAT|w32.TBSTYLE_TOOLTIPS|w32.TBSTYLE_WRAPABLE| + w32.WS_CHILD|w32.TBSTYLE_LIST) +} + +// NewToolbar creates toolbar with text below the image. +func NewToolbar(parent Controller) *Toolbar { + return newToolbar(parent, w32.CCS_NODIVIDER|w32.TBSTYLE_FLAT|w32.TBSTYLE_TOOLTIPS|w32.TBSTYLE_WRAPABLE| + w32.WS_CHILD /*|w32.TBSTYLE_TRANSPARENT*/) +} + +func newToolbar(parent Controller, style uint) *Toolbar { + tb := new(Toolbar) + + tb.InitControl("ToolbarWindow32", parent, 0, style) + + exStyle := w32.SendMessage(tb.hwnd, w32.TB_GETEXTENDEDSTYLE, 0, 0) + exStyle |= w32.TBSTYLE_EX_DRAWDDARROWS | w32.TBSTYLE_EX_MIXEDBUTTONS + w32.SendMessage(tb.hwnd, w32.TB_SETEXTENDEDSTYLE, 0, exStyle) + RegMsgHandler(tb) + + tb.SetFont(DefaultFont) + tb.SetPos(0, 0) + tb.SetSize(200, 40) + + return tb +} + +func (tb *Toolbar) SetImageList(imageList *ImageList) { + w32.SendMessage(tb.hwnd, w32.TB_SETIMAGELIST, 0, uintptr(imageList.Handle())) + tb.iml = imageList +} + +func (tb *Toolbar) initButton(btn *ToolButton, state, style *byte, image *int32, text *uintptr) { + *style |= w32.BTNS_AUTOSIZE + + if btn.checked { + *state |= w32.TBSTATE_CHECKED + } + + if btn.enabled { + *state |= w32.TBSTATE_ENABLED + } + + if btn.checkable { + *style |= w32.BTNS_CHECK + } + + if len(btn.Text()) > 0 { + *style |= w32.BTNS_SHOWTEXT + } + + if btn.IsSeparator() { + *style = w32.BTNS_SEP + } + + *image = int32(btn.Image()) + *text = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(btn.Text()))) +} + +func (tb *Toolbar) update(btn *ToolButton) { + tbbi := w32.TBBUTTONINFO{ + DwMask: w32.TBIF_IMAGE | w32.TBIF_STATE | w32.TBIF_STYLE | w32.TBIF_TEXT, + } + + tbbi.CbSize = uint32(unsafe.Sizeof(tbbi)) + + var i int + for i = range tb.buttons { + if tb.buttons[i] == btn { + break + } + } + + tb.initButton(btn, &tbbi.FsState, &tbbi.FsStyle, &tbbi.IImage, &tbbi.PszText) + if w32.SendMessage(tb.hwnd, w32.TB_SETBUTTONINFO, uintptr(i), uintptr(unsafe.Pointer(&tbbi))) == 0 { + panic("SendMessage(TB_SETBUTTONINFO) failed") + } +} + +func (tb *Toolbar) AddSeparator() { + tb.AddButton("-", 0) +} + +// AddButton creates and adds button to the toolbar. Use returned toolbutton to setup OnClick event. +func (tb *Toolbar) AddButton(text string, image int) *ToolButton { + bt := &ToolButton{ + tb: tb, // points to parent + text: text, + image: image, + enabled: true, + } + tb.buttons = append(tb.buttons, bt) + index := len(tb.buttons) - 1 + + tbb := w32.TBBUTTON{ + IdCommand: int32(index), + } + + tb.initButton(bt, &tbb.FsState, &tbb.FsStyle, &tbb.IBitmap, &tbb.IString) + w32.SendMessage(tb.hwnd, w32.TB_BUTTONSTRUCTSIZE, uintptr(unsafe.Sizeof(tbb)), 0) + + if w32.SendMessage(tb.hwnd, w32.TB_INSERTBUTTON, uintptr(index), uintptr(unsafe.Pointer(&tbb))) == w32.FALSE { + panic("SendMessage(TB_ADDBUTTONS)") + } + + w32.SendMessage(tb.hwnd, w32.TB_AUTOSIZE, 0, 0) + return bt +} + +func (tb *Toolbar) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + switch msg { + case w32.WM_COMMAND: + switch w32.HIWORD(uint32(wparam)) { + case w32.BN_CLICKED: + id := uint16(w32.LOWORD(uint32(wparam))) + btn := tb.buttons[id] + btn.onClick.Fire(NewEvent(tb, nil)) + } + } + return w32.DefWindowProc(tb.hwnd, msg, wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/tooltip.go b/v2/internal/frontend/desktop/windows/winc/tooltip.go new file mode 100644 index 000000000..f18ba1cda --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/tooltip.go @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "syscall" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +type ToolTip struct { + ControlBase +} + +func NewToolTip(parent Controller) *ToolTip { + tp := new(ToolTip) + + tp.InitControl("tooltips_class32", parent, w32.WS_EX_TOPMOST, w32.WS_POPUP|w32.TTS_NOPREFIX|w32.TTS_ALWAYSTIP) + w32.SetWindowPos(tp.Handle(), w32.HWND_TOPMOST, 0, 0, 0, 0, w32.SWP_NOMOVE|w32.SWP_NOSIZE|w32.SWP_NOACTIVATE) + + return tp +} + +func (tp *ToolTip) SetTip(tool Controller, tip string) bool { + var ti w32.TOOLINFO + ti.CbSize = uint32(unsafe.Sizeof(ti)) + if tool.Parent() != nil { + ti.Hwnd = tool.Parent().Handle() + } + ti.UFlags = w32.TTF_IDISHWND | w32.TTF_SUBCLASS /* | TTF_ABSOLUTE */ + ti.UId = uintptr(tool.Handle()) + ti.LpszText = syscall.StringToUTF16Ptr(tip) + + return w32.SendMessage(tp.Handle(), w32.TTM_ADDTOOL, 0, uintptr(unsafe.Pointer(&ti))) != w32.FALSE +} + +func (tp *ToolTip) WndProc(msg uint, wparam, lparam uintptr) uintptr { + return w32.DefWindowProc(tp.hwnd, uint32(msg), wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/treeview.go b/v2/internal/frontend/desktop/windows/winc/treeview.go new file mode 100644 index 000000000..4976fa65d --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/treeview.go @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + */ + +package winc + +import ( + "errors" + "syscall" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +// TreeItem represents an item in a TreeView widget. +type TreeItem interface { + Text() string // Text returns the text of the item. + ImageIndex() int // ImageIndex is used only if SetImageList is called on the treeview +} + +type treeViewItemInfo struct { + handle w32.HTREEITEM + child2Handle map[TreeItem]w32.HTREEITEM +} + +// StringTreeItem is helper for basic string lists. +type StringTreeItem struct { + Data string + Image int +} + +func (s StringTreeItem) Text() string { return s.Data } +func (s StringTreeItem) ImageIndex() int { return s.Image } + +type TreeView struct { + ControlBase + + iml *ImageList + item2Info map[TreeItem]*treeViewItemInfo + handle2Item map[w32.HTREEITEM]TreeItem + currItem TreeItem + + onSelectedChange EventManager + onExpand EventManager + onCollapse EventManager + onViewChange EventManager +} + +func NewTreeView(parent Controller) *TreeView { + tv := new(TreeView) + + tv.InitControl("SysTreeView32", parent, 0, w32.WS_CHILD|w32.WS_VISIBLE| + w32.WS_BORDER|w32.TVS_HASBUTTONS|w32.TVS_LINESATROOT|w32.TVS_SHOWSELALWAYS| + w32.TVS_TRACKSELECT /*|w32.WS_EX_CLIENTEDGE*/) + + tv.item2Info = make(map[TreeItem]*treeViewItemInfo) + tv.handle2Item = make(map[w32.HTREEITEM]TreeItem) + + RegMsgHandler(tv) + + tv.SetFont(DefaultFont) + tv.SetSize(200, 400) + + if err := tv.SetTheme("Explorer"); err != nil { + // theme error is ignored + } + return tv +} + +func (tv *TreeView) EnableDoubleBuffer(enable bool) { + if enable { + w32.SendMessage(tv.hwnd, w32.TVM_SETEXTENDEDSTYLE, 0, w32.TVS_EX_DOUBLEBUFFER) + } else { + w32.SendMessage(tv.hwnd, w32.TVM_SETEXTENDEDSTYLE, w32.TVS_EX_DOUBLEBUFFER, 0) + } +} + +// SelectedItem is current selected item after OnSelectedChange event. +func (tv *TreeView) SelectedItem() TreeItem { + return tv.currItem +} + +func (tv *TreeView) SetSelectedItem(item TreeItem) bool { + var handle w32.HTREEITEM + if item != nil { + if info := tv.item2Info[item]; info == nil { + return false // invalid item + } else { + handle = info.handle + } + } + + if w32.SendMessage(tv.hwnd, w32.TVM_SELECTITEM, w32.TVGN_CARET, uintptr(handle)) == 0 { + return false // set selected failed + } + tv.currItem = item + return true +} + +func (tv *TreeView) ItemAt(x, y int) TreeItem { + hti := w32.TVHITTESTINFO{Pt: w32.POINT{int32(x), int32(y)}} + w32.SendMessage(tv.hwnd, w32.TVM_HITTEST, 0, uintptr(unsafe.Pointer(&hti))) + if item, ok := tv.handle2Item[hti.HItem]; ok { + return item + } + return nil +} + +func (tv *TreeView) Items() (list []TreeItem) { + for item := range tv.item2Info { + list = append(list, item) + } + return list +} + +func (tv *TreeView) InsertItem(item, parent, insertAfter TreeItem) error { + var tvins w32.TVINSERTSTRUCT + tvi := &tvins.Item + + tvi.Mask = w32.TVIF_TEXT // w32.TVIF_CHILDREN | w32.TVIF_TEXT + tvi.PszText = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(item.Text()))) // w32.LPSTR_TEXTCALLBACK + tvi.CChildren = 0 // w32.I_CHILDRENCALLBACK + + if parent == nil { + tvins.HParent = w32.TVI_ROOT + } else { + info := tv.item2Info[parent] + if info == nil { + return errors.New("winc: invalid parent") + } + tvins.HParent = info.handle + } + + if insertAfter == nil { + tvins.HInsertAfter = w32.TVI_LAST + } else { + info := tv.item2Info[insertAfter] + if info == nil { + return errors.New("winc: invalid prev item") + } + tvins.HInsertAfter = info.handle + } + + tv.applyImage(tvi, item) + + hItem := w32.HTREEITEM(w32.SendMessage(tv.hwnd, w32.TVM_INSERTITEM, 0, uintptr(unsafe.Pointer(&tvins)))) + if hItem == 0 { + return errors.New("winc: TVM_INSERTITEM failed") + } + tv.item2Info[item] = &treeViewItemInfo{hItem, make(map[TreeItem]w32.HTREEITEM)} + tv.handle2Item[hItem] = item + return nil +} + +func (tv *TreeView) UpdateItem(item TreeItem) bool { + it := tv.item2Info[item] + if it == nil { + return false + } + + tvi := &w32.TVITEM{ + Mask: w32.TVIF_TEXT, + HItem: it.handle, + PszText: uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(item.Text()))), + } + tv.applyImage(tvi, item) + + if w32.SendMessage(tv.hwnd, w32.TVM_SETITEM, 0, uintptr(unsafe.Pointer(tvi))) == 0 { + return false + } + return true +} + +func (tv *TreeView) DeleteItem(item TreeItem) bool { + it := tv.item2Info[item] + if it == nil { + return false + } + + if w32.SendMessage(tv.hwnd, w32.TVM_DELETEITEM, 0, uintptr(it.handle)) == 0 { + return false + } + + delete(tv.item2Info, item) + delete(tv.handle2Item, it.handle) + return true +} + +func (tv *TreeView) DeleteAllItems() bool { + if w32.SendMessage(tv.hwnd, w32.TVM_DELETEITEM, 0, 0) == 0 { + return false + } + + tv.item2Info = make(map[TreeItem]*treeViewItemInfo) + tv.handle2Item = make(map[w32.HTREEITEM]TreeItem) + return true +} + +func (tv *TreeView) Expand(item TreeItem) bool { + if w32.SendMessage(tv.hwnd, w32.TVM_EXPAND, w32.TVE_EXPAND, uintptr(tv.item2Info[item].handle)) == 0 { + return false + } + return true +} + +func (tv *TreeView) Collapse(item TreeItem) bool { + if w32.SendMessage(tv.hwnd, w32.TVM_EXPAND, w32.TVE_COLLAPSE, uintptr(tv.item2Info[item].handle)) == 0 { + return false + } + return true +} + +func (tv *TreeView) EnsureVisible(item TreeItem) bool { + if info := tv.item2Info[item]; info != nil { + return w32.SendMessage(tv.hwnd, w32.TVM_ENSUREVISIBLE, 0, uintptr(info.handle)) != 0 + } + return false +} + +func (tv *TreeView) SetImageList(imageList *ImageList) { + w32.SendMessage(tv.hwnd, w32.TVM_SETIMAGELIST, 0, uintptr(imageList.Handle())) + tv.iml = imageList +} + +func (tv *TreeView) applyImage(tc *w32.TVITEM, item TreeItem) { + if tv.iml != nil { + tc.Mask |= w32.TVIF_IMAGE | w32.TVIF_SELECTEDIMAGE + tc.IImage = int32(item.ImageIndex()) + tc.ISelectedImage = int32(item.ImageIndex()) + } +} + +func (tv *TreeView) OnSelectedChange() *EventManager { + return &tv.onSelectedChange +} + +func (tv *TreeView) OnExpand() *EventManager { + return &tv.onExpand +} + +func (tv *TreeView) OnCollapse() *EventManager { + return &tv.onCollapse +} + +func (tv *TreeView) OnViewChange() *EventManager { + return &tv.onViewChange +} + +// Message processer +func (tv *TreeView) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + switch msg { + case w32.WM_NOTIFY: + nm := (*w32.NMHDR)(unsafe.Pointer(lparam)) + + switch nm.Code { + case w32.TVN_ITEMEXPANDED: + nmtv := (*w32.NMTREEVIEW)(unsafe.Pointer(lparam)) + + switch nmtv.Action { + case w32.TVE_COLLAPSE: + tv.onCollapse.Fire(NewEvent(tv, nil)) + + case w32.TVE_COLLAPSERESET: + + case w32.TVE_EXPAND: + tv.onExpand.Fire(NewEvent(tv, nil)) + + case w32.TVE_EXPANDPARTIAL: + + case w32.TVE_TOGGLE: + } + + case w32.TVN_SELCHANGED: + nmtv := (*w32.NMTREEVIEW)(unsafe.Pointer(lparam)) + tv.currItem = tv.handle2Item[nmtv.ItemNew.HItem] + tv.onSelectedChange.Fire(NewEvent(tv, nil)) + + case w32.TVN_GETDISPINFO: + tv.onViewChange.Fire(NewEvent(tv, nil)) + } + + } + return w32.DefWindowProc(tv.hwnd, msg, wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/winc/utils.go b/v2/internal/frontend/desktop/windows/winc/utils.go new file mode 100644 index 000000000..aa4304a1c --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/utils.go @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "fmt" + "syscall" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +func internalTrackMouseEvent(hwnd w32.HWND) { + var tme w32.TRACKMOUSEEVENT + tme.CbSize = uint32(unsafe.Sizeof(tme)) + tme.DwFlags = w32.TME_LEAVE + tme.HwndTrack = hwnd + tme.DwHoverTime = w32.HOVER_DEFAULT + + w32.TrackMouseEvent(&tme) +} + +func SetStyle(hwnd w32.HWND, b bool, style int) { + originalStyle := int(w32.GetWindowLongPtr(hwnd, w32.GWL_STYLE)) + if originalStyle != 0 { + if b { + originalStyle |= style + } else { + originalStyle &^= style + } + w32.SetWindowLongPtr(hwnd, w32.GWL_STYLE, uintptr(originalStyle)) + } +} + +func SetExStyle(hwnd w32.HWND, b bool, style int) { + originalStyle := int(w32.GetWindowLongPtr(hwnd, w32.GWL_EXSTYLE)) + if originalStyle != 0 { + if b { + originalStyle |= style + } else { + originalStyle &^= style + } + w32.SetWindowLongPtr(hwnd, w32.GWL_EXSTYLE, uintptr(originalStyle)) + } +} + +func CreateWindow(className string, parent Controller, exStyle, style uint) w32.HWND { + instance := GetAppInstance() + var parentHwnd w32.HWND + if parent != nil { + parentHwnd = parent.Handle() + } + var hwnd w32.HWND + hwnd = w32.CreateWindowEx( + exStyle, + syscall.StringToUTF16Ptr(className), + nil, + style, + w32.CW_USEDEFAULT, + w32.CW_USEDEFAULT, + w32.CW_USEDEFAULT, + w32.CW_USEDEFAULT, + parentHwnd, + 0, + instance, + nil) + + if hwnd == 0 { + errStr := fmt.Sprintf("Error occurred in CreateWindow(%s, %v, %d, %d)", className, parent, exStyle, style) + panic(errStr) + } + + return hwnd +} + +func RegisterClass(className string, wndproc uintptr) { + instance := GetAppInstance() + icon := w32.LoadIcon(instance, w32.MakeIntResource(w32.IDI_APPLICATION)) + + var wc w32.WNDCLASSEX + wc.Size = uint32(unsafe.Sizeof(wc)) + wc.Style = w32.CS_HREDRAW | w32.CS_VREDRAW + wc.WndProc = wndproc + wc.Instance = instance + wc.Background = w32.COLOR_BTNFACE + 1 + wc.Icon = icon + wc.Cursor = w32.LoadCursor(0, w32.MakeIntResource(w32.IDC_ARROW)) + wc.ClassName = syscall.StringToUTF16Ptr(className) + wc.MenuName = nil + wc.IconSm = icon + + if ret := w32.RegisterClassEx(&wc); ret == 0 { + panic(syscall.GetLastError()) + } +} + +func RegisterWindowMessage(name string) uint32 { + n := syscall.StringToUTF16Ptr(name) + + ret := w32.RegisterWindowMessage(n) + if ret == 0 { + panic(syscall.GetLastError()) + } + return ret +} + +func getMonitorInfo(hwnd w32.HWND) *w32.MONITORINFO { + currentMonitor := w32.MonitorFromWindow(hwnd, w32.MONITOR_DEFAULTTONEAREST) + var info w32.MONITORINFO + info.CbSize = uint32(unsafe.Sizeof(info)) + w32.GetMonitorInfo(currentMonitor, &info) + return &info +} +func getWindowInfo(hwnd w32.HWND) *w32.WINDOWINFO { + var info w32.WINDOWINFO + info.CbSize = uint32(unsafe.Sizeof(info)) + w32.GetWindowInfo(hwnd, &info) + return &info +} + +func RegClassOnlyOnce(className string) { + isExists := false + for _, class := range gRegisteredClasses { + if class == className { + isExists = true + break + } + } + + if !isExists { + RegisterClass(className, GeneralWndprocCallBack) + gRegisteredClasses = append(gRegisteredClasses, className) + } +} + +func ScreenToClientRect(hwnd w32.HWND, rect *w32.RECT) *Rect { + l, t, r, b := rect.Left, rect.Top, rect.Right, rect.Bottom + l1, t1, _ := w32.ScreenToClient(hwnd, int(l), int(t)) + r1, b1, _ := w32.ScreenToClient(hwnd, int(r), int(b)) + return NewRect(l1, t1, r1, b1) +} + +// ScaleWithDPI scales the pixels from the default DPI-Space (96) to the target DPI-Space. +func ScaleWithDPI(pixels int, dpi uint) int { + return (pixels * int(dpi)) / 96 +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/comctl32.go b/v2/internal/frontend/desktop/windows/winc/w32/comctl32.go new file mode 100644 index 000000000..0f37403a0 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/comctl32.go @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +import ( + "syscall" + "unsafe" +) + +var ( + modcomctl32 = syscall.NewLazyDLL("comctl32.dll") + + procInitCommonControlsEx = modcomctl32.NewProc("InitCommonControlsEx") + procImageList_Create = modcomctl32.NewProc("ImageList_Create") + procImageList_Destroy = modcomctl32.NewProc("ImageList_Destroy") + procImageList_GetImageCount = modcomctl32.NewProc("ImageList_GetImageCount") + procImageList_SetImageCount = modcomctl32.NewProc("ImageList_SetImageCount") + procImageList_Add = modcomctl32.NewProc("ImageList_Add") + procImageList_ReplaceIcon = modcomctl32.NewProc("ImageList_ReplaceIcon") + procImageList_Remove = modcomctl32.NewProc("ImageList_Remove") + procTrackMouseEvent = modcomctl32.NewProc("_TrackMouseEvent") +) + +func InitCommonControlsEx(lpInitCtrls *INITCOMMONCONTROLSEX) bool { + ret, _, _ := procInitCommonControlsEx.Call( + uintptr(unsafe.Pointer(lpInitCtrls))) + + return ret != 0 +} + +func ImageList_Create(cx, cy int, flags uint, cInitial, cGrow int) HIMAGELIST { + ret, _, _ := procImageList_Create.Call( + uintptr(cx), + uintptr(cy), + uintptr(flags), + uintptr(cInitial), + uintptr(cGrow)) + + if ret == 0 { + panic("Create image list failed") + } + + return HIMAGELIST(ret) +} + +func ImageList_Destroy(himl HIMAGELIST) bool { + ret, _, _ := procImageList_Destroy.Call( + uintptr(himl)) + + return ret != 0 +} + +func ImageList_GetImageCount(himl HIMAGELIST) int { + ret, _, _ := procImageList_GetImageCount.Call( + uintptr(himl)) + + return int(ret) +} + +func ImageList_SetImageCount(himl HIMAGELIST, uNewCount uint) bool { + ret, _, _ := procImageList_SetImageCount.Call( + uintptr(himl), + uintptr(uNewCount)) + + return ret != 0 +} + +func ImageList_Add(himl HIMAGELIST, hbmImage, hbmMask HBITMAP) int { + ret, _, _ := procImageList_Add.Call( + uintptr(himl), + uintptr(hbmImage), + uintptr(hbmMask)) + + return int(ret) +} + +func ImageList_ReplaceIcon(himl HIMAGELIST, i int, hicon HICON) int { + ret, _, _ := procImageList_ReplaceIcon.Call( + uintptr(himl), + uintptr(i), + uintptr(hicon)) + + return int(ret) +} + +func ImageList_AddIcon(himl HIMAGELIST, hicon HICON) int { + return ImageList_ReplaceIcon(himl, -1, hicon) +} + +func ImageList_Remove(himl HIMAGELIST, i int) bool { + ret, _, _ := procImageList_Remove.Call( + uintptr(himl), + uintptr(i)) + + return ret != 0 +} + +func ImageList_RemoveAll(himl HIMAGELIST) bool { + return ImageList_Remove(himl, -1) +} + +func TrackMouseEvent(tme *TRACKMOUSEEVENT) bool { + ret, _, _ := procTrackMouseEvent.Call( + uintptr(unsafe.Pointer(tme))) + + return ret != 0 +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/comdlg32.go b/v2/internal/frontend/desktop/windows/winc/w32/comdlg32.go new file mode 100644 index 000000000..936bcf33b --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/comdlg32.go @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +import ( + "syscall" + "unsafe" +) + +var ( + modcomdlg32 = syscall.NewLazyDLL("comdlg32.dll") + + procGetSaveFileName = modcomdlg32.NewProc("GetSaveFileNameW") + procGetOpenFileName = modcomdlg32.NewProc("GetOpenFileNameW") + procCommDlgExtendedError = modcomdlg32.NewProc("CommDlgExtendedError") +) + +func GetOpenFileName(ofn *OPENFILENAME) bool { + ret, _, _ := procGetOpenFileName.Call( + uintptr(unsafe.Pointer(ofn))) + + return ret != 0 +} + +func GetSaveFileName(ofn *OPENFILENAME) bool { + ret, _, _ := procGetSaveFileName.Call( + uintptr(unsafe.Pointer(ofn))) + + return ret != 0 +} + +func CommDlgExtendedError() uint { + ret, _, _ := procCommDlgExtendedError.Call() + + return uint(ret) +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/constants.go b/v2/internal/frontend/desktop/windows/winc/w32/constants.go new file mode 100644 index 000000000..c8191ad5c --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/constants.go @@ -0,0 +1,3525 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +const ( + FALSE = 0 + TRUE = 1 +) + +const ( + NO_ERROR = 0 + ERROR_SUCCESS = 0 + ERROR_FILE_NOT_FOUND = 2 + ERROR_PATH_NOT_FOUND = 3 + ERROR_ACCESS_DENIED = 5 + ERROR_INVALID_HANDLE = 6 + ERROR_BAD_FORMAT = 11 + ERROR_INVALID_NAME = 123 + ERROR_MORE_DATA = 234 + ERROR_NO_MORE_ITEMS = 259 + ERROR_INVALID_SERVICE_CONTROL = 1052 + ERROR_SERVICE_REQUEST_TIMEOUT = 1053 + ERROR_SERVICE_NO_THREAD = 1054 + ERROR_SERVICE_DATABASE_LOCKED = 1055 + ERROR_SERVICE_ALREADY_RUNNING = 1056 + ERROR_SERVICE_DISABLED = 1058 + ERROR_SERVICE_DOES_NOT_EXIST = 1060 + ERROR_SERVICE_CANNOT_ACCEPT_CTRL = 1061 + ERROR_SERVICE_NOT_ACTIVE = 1062 + ERROR_DATABASE_DOES_NOT_EXIST = 1065 + ERROR_SERVICE_DEPENDENCY_FAIL = 1068 + ERROR_SERVICE_LOGON_FAILED = 1069 + ERROR_SERVICE_MARKED_FOR_DELETE = 1072 + ERROR_SERVICE_DEPENDENCY_DELETED = 1075 +) + +const ( + SE_ERR_FNF = 2 + SE_ERR_PNF = 3 + SE_ERR_ACCESSDENIED = 5 + SE_ERR_OOM = 8 + SE_ERR_DLLNOTFOUND = 32 + SE_ERR_SHARE = 26 + SE_ERR_ASSOCINCOMPLETE = 27 + SE_ERR_DDETIMEOUT = 28 + SE_ERR_DDEFAIL = 29 + SE_ERR_DDEBUSY = 30 + SE_ERR_NOASSOC = 31 +) + +const ( + CW_USEDEFAULT = ^0x7fffffff +) + +const ( + IMAGE_BITMAP = 0 + IMAGE_ICON = 1 + IMAGE_CURSOR = 2 + IMAGE_ENHMETAFILE = 3 +) + +// ShowWindow constants +const ( + SW_HIDE = 0 + SW_NORMAL = 1 + SW_SHOWNORMAL = 1 + SW_SHOWMINIMIZED = 2 + SW_MAXIMIZE = 3 + SW_SHOWMAXIMIZED = 3 + SW_SHOWNOACTIVATE = 4 + SW_SHOW = 5 + SW_MINIMIZE = 6 + SW_SHOWMINNOACTIVE = 7 + SW_SHOWNA = 8 + SW_RESTORE = 9 + SW_SHOWDEFAULT = 10 + SW_FORCEMINIMIZE = 11 +) + +// Window class styles +const ( + CS_VREDRAW = 0x00000001 + CS_HREDRAW = 0x00000002 + CS_KEYCVTWINDOW = 0x00000004 + CS_DBLCLKS = 0x00000008 + CS_OWNDC = 0x00000020 + CS_CLASSDC = 0x00000040 + CS_PARENTDC = 0x00000080 + CS_NOKEYCVT = 0x00000100 + CS_NOCLOSE = 0x00000200 + CS_SAVEBITS = 0x00000800 + CS_BYTEALIGNCLIENT = 0x00001000 + CS_BYTEALIGNWINDOW = 0x00002000 + CS_GLOBALCLASS = 0x00004000 + CS_IME = 0x00010000 + CS_DROPSHADOW = 0x00020000 +) + +// Predefined cursor constants +const ( + IDC_ARROW = 32512 + IDC_IBEAM = 32513 + IDC_WAIT = 32514 + IDC_CROSS = 32515 + IDC_UPARROW = 32516 + IDC_SIZENWSE = 32642 + IDC_SIZENESW = 32643 + IDC_SIZEWE = 32644 + IDC_SIZENS = 32645 + IDC_SIZEALL = 32646 + IDC_NO = 32648 + IDC_HAND = 32649 + IDC_APPSTARTING = 32650 + IDC_HELP = 32651 + IDC_ICON = 32641 + IDC_SIZE = 32640 +) + +// Predefined icon constants +const ( + IDI_APPLICATION = 32512 + IDI_HAND = 32513 + IDI_QUESTION = 32514 + IDI_EXCLAMATION = 32515 + IDI_ASTERISK = 32516 + IDI_WINLOGO = 32517 + IDI_WARNING = IDI_EXCLAMATION + IDI_ERROR = IDI_HAND + IDI_INFORMATION = IDI_ASTERISK +) + +// Button style constants +const ( + BS_3STATE = 5 + BS_AUTO3STATE = 6 + BS_AUTOCHECKBOX = 3 + BS_AUTORADIOBUTTON = 9 + BS_BITMAP = 128 + BS_BOTTOM = 0x800 + BS_CENTER = 0x300 + BS_CHECKBOX = 2 + BS_DEFPUSHBUTTON = 1 + BS_GROUPBOX = 7 + BS_ICON = 64 + BS_LEFT = 256 + BS_LEFTTEXT = 32 + BS_MULTILINE = 0x2000 + BS_NOTIFY = 0x4000 + BS_OWNERDRAW = 0xB + BS_PUSHBUTTON = 0 + BS_PUSHLIKE = 4096 + BS_RADIOBUTTON = 4 + BS_RIGHT = 512 + BS_RIGHTBUTTON = 32 + BS_TEXT = 0 + BS_TOP = 0x400 + BS_USERBUTTON = 8 + BS_VCENTER = 0xC00 + BS_FLAT = 0x8000 + BS_SPLITBUTTON = 0x000C // >= Vista + BS_DEFSPLITBUTTON = 0x000D // >= Vista +) + +// Button state constants +const ( + BST_CHECKED = 1 + BST_INDETERMINATE = 2 + BST_UNCHECKED = 0 + BST_FOCUS = 8 + BST_PUSHED = 4 +) + +// Predefined brushes constants +const ( + COLOR_3DDKSHADOW = 21 + COLOR_3DFACE = 15 + COLOR_3DHILIGHT = 20 + COLOR_3DHIGHLIGHT = 20 + COLOR_3DLIGHT = 22 + COLOR_BTNHILIGHT = 20 + COLOR_3DSHADOW = 16 + COLOR_ACTIVEBORDER = 10 + COLOR_ACTIVECAPTION = 2 + COLOR_APPWORKSPACE = 12 + COLOR_BACKGROUND = 1 + COLOR_DESKTOP = 1 + COLOR_BTNFACE = 15 + COLOR_BTNHIGHLIGHT = 20 + COLOR_BTNSHADOW = 16 + COLOR_BTNTEXT = 18 + COLOR_CAPTIONTEXT = 9 + COLOR_GRAYTEXT = 17 + COLOR_HIGHLIGHT = 13 + COLOR_HIGHLIGHTTEXT = 14 + COLOR_INACTIVEBORDER = 11 + COLOR_INACTIVECAPTION = 3 + COLOR_INACTIVECAPTIONTEXT = 19 + COLOR_INFOBK = 24 + COLOR_INFOTEXT = 23 + COLOR_MENU = 4 + COLOR_MENUTEXT = 7 + COLOR_SCROLLBAR = 0 + COLOR_WINDOW = 5 + COLOR_WINDOWFRAME = 6 + COLOR_WINDOWTEXT = 8 + COLOR_HOTLIGHT = 26 + COLOR_GRADIENTACTIVECAPTION = 27 + COLOR_GRADIENTINACTIVECAPTION = 28 +) + +// Button message constants +const ( + BM_CLICK = 245 + BM_GETCHECK = 240 + BM_GETIMAGE = 246 + BM_GETSTATE = 242 + BM_SETCHECK = 241 + BM_SETIMAGE = 247 + BM_SETSTATE = 243 + BM_SETSTYLE = 244 +) + +// Button notifications +const ( + BN_CLICKED = 0 + BN_PAINT = 1 + BN_HILITE = 2 + BN_PUSHED = BN_HILITE + BN_UNHILITE = 3 + BN_UNPUSHED = BN_UNHILITE + BN_DISABLE = 4 + BN_DOUBLECLICKED = 5 + BN_DBLCLK = BN_DOUBLECLICKED + BN_SETFOCUS = 6 + BN_KILLFOCUS = 7 +) + +// TrackPopupMenu[Ex] flags +const ( + TPM_CENTERALIGN = 0x0004 + TPM_LEFTALIGN = 0x0000 + TPM_RIGHTALIGN = 0x0008 + TPM_BOTTOMALIGN = 0x0020 + TPM_TOPALIGN = 0x0000 + TPM_VCENTERALIGN = 0x0010 + TPM_NONOTIFY = 0x0080 + TPM_RETURNCMD = 0x0100 + TPM_LEFTBUTTON = 0x0000 + TPM_RIGHTBUTTON = 0x0002 + TPM_HORNEGANIMATION = 0x0800 + TPM_HORPOSANIMATION = 0x0400 + TPM_NOANIMATION = 0x4000 + TPM_VERNEGANIMATION = 0x2000 + TPM_VERPOSANIMATION = 0x1000 + TPM_HORIZONTAL = 0x0000 + TPM_VERTICAL = 0x0040 +) + +// GetWindowLong and GetWindowLongPtr constants +const ( + GWL_EXSTYLE = -20 + GWL_STYLE = -16 + GWL_WNDPROC = -4 + GWLP_WNDPROC = -4 + GWL_HINSTANCE = -6 + GWLP_HINSTANCE = -6 + GWL_HWNDPARENT = -8 + GWLP_HWNDPARENT = -8 + GWL_ID = -12 + GWLP_ID = -12 + GWL_USERDATA = -21 + GWLP_USERDATA = -21 +) + +// Window style constants +const ( + WS_OVERLAPPED = 0x00000000 + WS_POPUP = 0x80000000 + WS_CHILD = 0x40000000 + WS_MINIMIZE = 0x20000000 + WS_VISIBLE = 0x10000000 + WS_DISABLED = 0x08000000 + WS_CLIPSIBLINGS = 0x04000000 + WS_CLIPCHILDREN = 0x02000000 + WS_MAXIMIZE = 0x01000000 + WS_CAPTION = 0x00C00000 + WS_BORDER = 0x00800000 + WS_DLGFRAME = 0x00400000 + WS_VSCROLL = 0x00200000 + WS_HSCROLL = 0x00100000 + WS_SYSMENU = 0x00080000 + WS_THICKFRAME = 0x00040000 + WS_GROUP = 0x00020000 + WS_TABSTOP = 0x00010000 + WS_MINIMIZEBOX = 0x00020000 + WS_MAXIMIZEBOX = 0x00010000 + WS_TILED = 0x00000000 + WS_ICONIC = 0x20000000 + WS_SIZEBOX = 0x00040000 + WS_OVERLAPPEDWINDOW = 0x00000000 | 0x00C00000 | 0x00080000 | 0x00040000 | 0x00020000 | 0x00010000 + WS_POPUPWINDOW = 0x80000000 | 0x00800000 | 0x00080000 + WS_CHILDWINDOW = 0x40000000 +) + +// Extended window style constants +const ( + WS_EX_DLGMODALFRAME = 0x00000001 + WS_EX_NOPARENTNOTIFY = 0x00000004 + WS_EX_TOPMOST = 0x00000008 + WS_EX_ACCEPTFILES = 0x00000010 + WS_EX_TRANSPARENT = 0x00000020 + WS_EX_MDICHILD = 0x00000040 + WS_EX_TOOLWINDOW = 0x00000080 + WS_EX_WINDOWEDGE = 0x00000100 + WS_EX_CLIENTEDGE = 0x00000200 + WS_EX_CONTEXTHELP = 0x00000400 + WS_EX_RIGHT = 0x00001000 + WS_EX_LEFT = 0x00000000 + WS_EX_RTLREADING = 0x00002000 + WS_EX_LTRREADING = 0x00000000 + WS_EX_LEFTSCROLLBAR = 0x00004000 + WS_EX_RIGHTSCROLLBAR = 0x00000000 + WS_EX_CONTROLPARENT = 0x00010000 + WS_EX_STATICEDGE = 0x00020000 + WS_EX_APPWINDOW = 0x00040000 + WS_EX_OVERLAPPEDWINDOW = 0x00000100 | 0x00000200 + WS_EX_PALETTEWINDOW = 0x00000100 | 0x00000080 | 0x00000008 + WS_EX_LAYERED = 0x00080000 + WS_EX_NOINHERITLAYOUT = 0x00100000 + WS_EX_NOREDIRECTIONBITMAP = 0x00200000 + WS_EX_LAYOUTRTL = 0x00400000 + WS_EX_NOACTIVATE = 0x08000000 +) + +// Window message constants +const ( + WM_APP = 32768 + WM_ACTIVATE = 6 + WM_ACTIVATEAPP = 28 + WM_AFXFIRST = 864 + WM_AFXLAST = 895 + WM_ASKCBFORMATNAME = 780 + WM_CANCELJOURNAL = 75 + WM_CANCELMODE = 31 + WM_CAPTURECHANGED = 533 + WM_CHANGECBCHAIN = 781 + WM_CHAR = 258 + WM_CHARTOITEM = 47 + WM_CHILDACTIVATE = 34 + WM_CLEAR = 771 + WM_CLOSE = 16 + WM_COMMAND = 273 + WM_COMMNOTIFY = 68 /* OBSOLETE */ + WM_COMPACTING = 65 + WM_COMPAREITEM = 57 + WM_CONTEXTMENU = 123 + WM_COPY = 769 + WM_COPYDATA = 74 + WM_CREATE = 1 + WM_CTLCOLORBTN = 309 + WM_CTLCOLORDLG = 310 + WM_CTLCOLOREDIT = 307 + WM_CTLCOLORLISTBOX = 308 + WM_CTLCOLORMSGBOX = 306 + WM_CTLCOLORSCROLLBAR = 311 + WM_CTLCOLORSTATIC = 312 + WM_CUT = 768 + WM_DEADCHAR = 259 + WM_DELETEITEM = 45 + WM_DESTROY = 2 + WM_DESTROYCLIPBOARD = 775 + WM_DEVICECHANGE = 537 + WM_DEVMODECHANGE = 27 + WM_DISPLAYCHANGE = 126 + WM_DRAWCLIPBOARD = 776 + WM_DRAWITEM = 43 + WM_DROPFILES = 563 + WM_ENABLE = 10 + WM_ENDSESSION = 22 + WM_ENTERIDLE = 289 + WM_ENTERMENULOOP = 529 + WM_ENTERSIZEMOVE = 561 + WM_ERASEBKGND = 20 + WM_EXITMENULOOP = 530 + WM_EXITSIZEMOVE = 562 + WM_FONTCHANGE = 29 + WM_GETDLGCODE = 135 + WM_GETFONT = 49 + WM_GETHOTKEY = 51 + WM_GETICON = 127 + WM_GETMINMAXINFO = 36 + WM_GETTEXT = 13 + WM_GETTEXTLENGTH = 14 + WM_HANDHELDFIRST = 856 + WM_HANDHELDLAST = 863 + WM_HELP = 83 + WM_HOTKEY = 786 + WM_HSCROLL = 276 + WM_HSCROLLCLIPBOARD = 782 + WM_ICONERASEBKGND = 39 + WM_INITDIALOG = 272 + WM_INITMENU = 278 + WM_INITMENUPOPUP = 279 + WM_INPUT = 0x00FF + WM_INPUTLANGCHANGE = 81 + WM_INPUTLANGCHANGEREQUEST = 80 + WM_KEYDOWN = 256 + WM_KEYUP = 257 + WM_KILLFOCUS = 8 + WM_MDIACTIVATE = 546 + WM_MDICASCADE = 551 + WM_MDICREATE = 544 + WM_MDIDESTROY = 545 + WM_MDIGETACTIVE = 553 + WM_MDIICONARRANGE = 552 + WM_MDIMAXIMIZE = 549 + WM_MDINEXT = 548 + WM_MDIREFRESHMENU = 564 + WM_MDIRESTORE = 547 + WM_MDISETMENU = 560 + WM_MDITILE = 550 + WM_MEASUREITEM = 44 + WM_GETOBJECT = 0x003D + WM_CHANGEUISTATE = 0x0127 + WM_UPDATEUISTATE = 0x0128 + WM_QUERYUISTATE = 0x0129 + WM_UNINITMENUPOPUP = 0x0125 + WM_MENURBUTTONUP = 290 + WM_MENUCOMMAND = 0x0126 + WM_MENUGETOBJECT = 0x0124 + WM_MENUDRAG = 0x0123 + WM_APPCOMMAND = 0x0319 + WM_MENUCHAR = 288 + WM_MENUSELECT = 287 + WM_MOVE = 3 + WM_MOVING = 534 + WM_NCACTIVATE = 134 + WM_NCCALCSIZE = 131 + WM_NCCREATE = 129 + WM_NCDESTROY = 130 + WM_NCHITTEST = 132 + WM_NCLBUTTONDBLCLK = 163 + WM_NCLBUTTONDOWN = 161 + WM_NCLBUTTONUP = 162 + WM_NCMBUTTONDBLCLK = 169 + WM_NCMBUTTONDOWN = 167 + WM_NCMBUTTONUP = 168 + WM_NCXBUTTONDOWN = 171 + WM_NCXBUTTONUP = 172 + WM_NCXBUTTONDBLCLK = 173 + WM_NCMOUSEHOVER = 0x02A0 + WM_NCMOUSELEAVE = 0x02A2 + WM_NCMOUSEMOVE = 160 + WM_NCPAINT = 133 + WM_NCRBUTTONDBLCLK = 166 + WM_NCRBUTTONDOWN = 164 + WM_NCRBUTTONUP = 165 + WM_NEXTDLGCTL = 40 + WM_NEXTMENU = 531 + WM_NOTIFY = 78 + WM_NOTIFYFORMAT = 85 + WM_NULL = 0 + WM_PAINT = 15 + WM_PAINTCLIPBOARD = 777 + WM_PAINTICON = 38 + WM_PALETTECHANGED = 785 + WM_PALETTEISCHANGING = 784 + WM_PARENTNOTIFY = 528 + WM_PASTE = 770 + WM_PENWINFIRST = 896 + WM_PENWINLAST = 911 + WM_POWER = 72 + WM_POWERBROADCAST = 536 + WM_PRINT = 791 + WM_PRINTCLIENT = 792 + WM_QUERYDRAGICON = 55 + WM_QUERYENDSESSION = 17 + WM_QUERYNEWPALETTE = 783 + WM_QUERYOPEN = 19 + WM_QUEUESYNC = 35 + WM_QUIT = 18 + WM_RENDERALLFORMATS = 774 + WM_RENDERFORMAT = 773 + WM_SETCURSOR = 32 + WM_SETFOCUS = 7 + WM_SETFONT = 48 + WM_SETHOTKEY = 50 + WM_SETICON = 128 + WM_SETREDRAW = 11 + WM_SETTEXT = 12 + WM_SETTINGCHANGE = 26 + WM_SHOWWINDOW = 24 + WM_SIZE = 5 + WM_SIZECLIPBOARD = 779 + WM_SIZING = 532 + WM_SPOOLERSTATUS = 42 + WM_STYLECHANGED = 125 + WM_STYLECHANGING = 124 + WM_SYSCHAR = 262 + WM_SYSCOLORCHANGE = 21 + WM_SYSCOMMAND = 274 + WM_SYSDEADCHAR = 263 + WM_SYSKEYDOWN = 260 + WM_SYSKEYUP = 261 + WM_TCARD = 82 + WM_THEMECHANGED = 794 + WM_TIMECHANGE = 30 + WM_TIMER = 275 + WM_UNDO = 772 + WM_USER = 1024 + WM_USERCHANGED = 84 + WM_VKEYTOITEM = 46 + WM_VSCROLL = 277 + WM_VSCROLLCLIPBOARD = 778 + WM_WINDOWPOSCHANGED = 71 + WM_WINDOWPOSCHANGING = 70 + WM_WININICHANGE = 26 + WM_KEYFIRST = 256 + WM_KEYLAST = 264 + WM_SYNCPAINT = 136 + WM_MOUSEACTIVATE = 33 + WM_MOUSEMOVE = 512 + WM_LBUTTONDOWN = 513 + WM_LBUTTONUP = 514 + WM_LBUTTONDBLCLK = 515 + WM_RBUTTONDOWN = 516 + WM_RBUTTONUP = 517 + WM_RBUTTONDBLCLK = 518 + WM_MBUTTONDOWN = 519 + WM_MBUTTONUP = 520 + WM_MBUTTONDBLCLK = 521 + WM_MOUSEWHEEL = 522 + WM_MOUSEFIRST = 512 + WM_XBUTTONDOWN = 523 + WM_XBUTTONUP = 524 + WM_XBUTTONDBLCLK = 525 + WM_MOUSELAST = 525 + WM_MOUSEHOVER = 0x2A1 + WM_MOUSELEAVE = 0x2A3 + WM_CLIPBOARDUPDATE = 0x031D +) + +// WM_ACTIVATE +const ( + WA_INACTIVE = 0 + WA_ACTIVE = 1 + WA_CLICKACTIVE = 2 +) + +const LF_FACESIZE = 32 + +// Font weight constants +const ( + FW_DONTCARE = 0 + FW_THIN = 100 + FW_EXTRALIGHT = 200 + FW_ULTRALIGHT = FW_EXTRALIGHT + FW_LIGHT = 300 + FW_NORMAL = 400 + FW_REGULAR = 400 + FW_MEDIUM = 500 + FW_SEMIBOLD = 600 + FW_DEMIBOLD = FW_SEMIBOLD + FW_BOLD = 700 + FW_EXTRABOLD = 800 + FW_ULTRABOLD = FW_EXTRABOLD + FW_HEAVY = 900 + FW_BLACK = FW_HEAVY +) + +// Charset constants +const ( + ANSI_CHARSET = 0 + DEFAULT_CHARSET = 1 + SYMBOL_CHARSET = 2 + SHIFTJIS_CHARSET = 128 + HANGEUL_CHARSET = 129 + HANGUL_CHARSET = 129 + GB2312_CHARSET = 134 + CHINESEBIG5_CHARSET = 136 + GREEK_CHARSET = 161 + TURKISH_CHARSET = 162 + HEBREW_CHARSET = 177 + ARABIC_CHARSET = 178 + BALTIC_CHARSET = 186 + RUSSIAN_CHARSET = 204 + THAI_CHARSET = 222 + EASTEUROPE_CHARSET = 238 + OEM_CHARSET = 255 + JOHAB_CHARSET = 130 + VIETNAMESE_CHARSET = 163 + MAC_CHARSET = 77 +) + +// Font output precision constants +const ( + OUT_DEFAULT_PRECIS = 0 + OUT_STRING_PRECIS = 1 + OUT_CHARACTER_PRECIS = 2 + OUT_STROKE_PRECIS = 3 + OUT_TT_PRECIS = 4 + OUT_DEVICE_PRECIS = 5 + OUT_RASTER_PRECIS = 6 + OUT_TT_ONLY_PRECIS = 7 + OUT_OUTLINE_PRECIS = 8 + OUT_PS_ONLY_PRECIS = 10 +) + +// Font clipping precision constants +const ( + CLIP_DEFAULT_PRECIS = 0 + CLIP_CHARACTER_PRECIS = 1 + CLIP_STROKE_PRECIS = 2 + CLIP_MASK = 15 + CLIP_LH_ANGLES = 16 + CLIP_TT_ALWAYS = 32 + CLIP_EMBEDDED = 128 +) + +// Font output quality constants +const ( + DEFAULT_QUALITY = 0 + DRAFT_QUALITY = 1 + PROOF_QUALITY = 2 + NONANTIALIASED_QUALITY = 3 + ANTIALIASED_QUALITY = 4 + CLEARTYPE_QUALITY = 5 +) + +// Font pitch constants +const ( + DEFAULT_PITCH = 0 + FIXED_PITCH = 1 + VARIABLE_PITCH = 2 +) + +// Font family constants +const ( + FF_DECORATIVE = 80 + FF_DONTCARE = 0 + FF_MODERN = 48 + FF_ROMAN = 16 + FF_SCRIPT = 64 + FF_SWISS = 32 +) + +// DeviceCapabilities capabilities +const ( + DC_FIELDS = 1 + DC_PAPERS = 2 + DC_PAPERSIZE = 3 + DC_MINEXTENT = 4 + DC_MAXEXTENT = 5 + DC_BINS = 6 + DC_DUPLEX = 7 + DC_SIZE = 8 + DC_EXTRA = 9 + DC_VERSION = 10 + DC_DRIVER = 11 + DC_BINNAMES = 12 + DC_ENUMRESOLUTIONS = 13 + DC_FILEDEPENDENCIES = 14 + DC_TRUETYPE = 15 + DC_PAPERNAMES = 16 + DC_ORIENTATION = 17 + DC_COPIES = 18 + DC_BINADJUST = 19 + DC_EMF_COMPLIANT = 20 + DC_DATATYPE_PRODUCED = 21 + DC_COLLATE = 22 + DC_MANUFACTURER = 23 + DC_MODEL = 24 + DC_PERSONALITY = 25 + DC_PRINTRATE = 26 + DC_PRINTRATEUNIT = 27 + DC_PRINTERMEM = 28 + DC_MEDIAREADY = 29 + DC_STAPLE = 30 + DC_PRINTRATEPPM = 31 + DC_COLORDEVICE = 32 + DC_NUP = 33 + DC_MEDIATYPENAMES = 34 + DC_MEDIATYPES = 35 +) + +// GetDeviceCaps index constants +const ( + DRIVERVERSION = 0 + TECHNOLOGY = 2 + HORZSIZE = 4 + VERTSIZE = 6 + HORZRES = 8 + VERTRES = 10 + LOGPIXELSX = 88 + LOGPIXELSY = 90 + BITSPIXEL = 12 + PLANES = 14 + NUMBRUSHES = 16 + NUMPENS = 18 + NUMFONTS = 22 + NUMCOLORS = 24 + NUMMARKERS = 20 + ASPECTX = 40 + ASPECTY = 42 + ASPECTXY = 44 + PDEVICESIZE = 26 + CLIPCAPS = 36 + SIZEPALETTE = 104 + NUMRESERVED = 106 + COLORRES = 108 + PHYSICALWIDTH = 110 + PHYSICALHEIGHT = 111 + PHYSICALOFFSETX = 112 + PHYSICALOFFSETY = 113 + SCALINGFACTORX = 114 + SCALINGFACTORY = 115 + VREFRESH = 116 + DESKTOPHORZRES = 118 + DESKTOPVERTRES = 117 + BLTALIGNMENT = 119 + SHADEBLENDCAPS = 120 + COLORMGMTCAPS = 121 + RASTERCAPS = 38 + CURVECAPS = 28 + LINECAPS = 30 + POLYGONALCAPS = 32 + TEXTCAPS = 34 +) + +// GetDeviceCaps TECHNOLOGY constants +const ( + DT_PLOTTER = 0 + DT_RASDISPLAY = 1 + DT_RASPRINTER = 2 + DT_RASCAMERA = 3 + DT_CHARSTREAM = 4 + DT_METAFILE = 5 + DT_DISPFILE = 6 +) + +// GetDeviceCaps SHADEBLENDCAPS constants +const ( + SB_NONE = 0x00 + SB_CONST_ALPHA = 0x01 + SB_PIXEL_ALPHA = 0x02 + SB_PREMULT_ALPHA = 0x04 + SB_GRAD_RECT = 0x10 + SB_GRAD_TRI = 0x20 +) + +// GetDeviceCaps COLORMGMTCAPS constants +const ( + CM_NONE = 0x00 + CM_DEVICE_ICM = 0x01 + CM_GAMMA_RAMP = 0x02 + CM_CMYK_COLOR = 0x04 +) + +// GetDeviceCaps RASTERCAPS constants +const ( + RC_BANDING = 2 + RC_BITBLT = 1 + RC_BITMAP64 = 8 + RC_DI_BITMAP = 128 + RC_DIBTODEV = 512 + RC_FLOODFILL = 4096 + RC_GDI20_OUTPUT = 16 + RC_PALETTE = 256 + RC_SCALING = 4 + RC_STRETCHBLT = 2048 + RC_STRETCHDIB = 8192 + RC_DEVBITS = 0x8000 + RC_OP_DX_OUTPUT = 0x4000 +) + +// GetDeviceCaps CURVECAPS constants +const ( + CC_NONE = 0 + CC_CIRCLES = 1 + CC_PIE = 2 + CC_CHORD = 4 + CC_ELLIPSES = 8 + CC_WIDE = 16 + CC_STYLED = 32 + CC_WIDESTYLED = 64 + CC_INTERIORS = 128 + CC_ROUNDRECT = 256 +) + +// GetDeviceCaps LINECAPS constants +const ( + LC_NONE = 0 + LC_POLYLINE = 2 + LC_MARKER = 4 + LC_POLYMARKER = 8 + LC_WIDE = 16 + LC_STYLED = 32 + LC_WIDESTYLED = 64 + LC_INTERIORS = 128 +) + +// GetDeviceCaps POLYGONALCAPS constants +const ( + PC_NONE = 0 + PC_POLYGON = 1 + PC_POLYPOLYGON = 256 + PC_PATHS = 512 + PC_RECTANGLE = 2 + PC_WINDPOLYGON = 4 + PC_SCANLINE = 8 + PC_TRAPEZOID = 4 + PC_WIDE = 16 + PC_STYLED = 32 + PC_WIDESTYLED = 64 + PC_INTERIORS = 128 +) + +// GetDeviceCaps TEXTCAPS constants +const ( + TC_OP_CHARACTER = 1 + TC_OP_STROKE = 2 + TC_CP_STROKE = 4 + TC_CR_90 = 8 + TC_CR_ANY = 16 + TC_SF_X_YINDEP = 32 + TC_SA_DOUBLE = 64 + TC_SA_INTEGER = 128 + TC_SA_CONTIN = 256 + TC_EA_DOUBLE = 512 + TC_IA_ABLE = 1024 + TC_UA_ABLE = 2048 + TC_SO_ABLE = 4096 + TC_RA_ABLE = 8192 + TC_VA_ABLE = 16384 + TC_RESERVED = 32768 + TC_SCROLLBLT = 65536 +) + +// Static control styles +const ( + SS_BITMAP = 14 + SS_BLACKFRAME = 7 + SS_BLACKRECT = 4 + SS_CENTER = 1 + SS_CENTERIMAGE = 512 + SS_EDITCONTROL = 0x2000 + SS_ENHMETAFILE = 15 + SS_ETCHEDFRAME = 18 + SS_ETCHEDHORZ = 16 + SS_ETCHEDVERT = 17 + SS_GRAYFRAME = 8 + SS_GRAYRECT = 5 + SS_ICON = 3 + SS_LEFT = 0 + SS_LEFTNOWORDWRAP = 0xc + SS_NOPREFIX = 128 + SS_NOTIFY = 256 + SS_OWNERDRAW = 0xd + SS_REALSIZECONTROL = 0x040 + SS_REALSIZEIMAGE = 0x800 + SS_RIGHT = 2 + SS_RIGHTJUST = 0x400 + SS_SIMPLE = 11 + SS_SUNKEN = 4096 + SS_WHITEFRAME = 9 + SS_WHITERECT = 6 + SS_USERITEM = 10 + SS_TYPEMASK = 0x0000001F + SS_ENDELLIPSIS = 0x00004000 + SS_PATHELLIPSIS = 0x00008000 + SS_WORDELLIPSIS = 0x0000C000 + SS_ELLIPSISMASK = 0x0000C000 +) + +// Edit styles +const ( + ES_LEFT = 0x0000 + ES_CENTER = 0x0001 + ES_RIGHT = 0x0002 + ES_MULTILINE = 0x0004 + ES_UPPERCASE = 0x0008 + ES_LOWERCASE = 0x0010 + ES_PASSWORD = 0x0020 + ES_AUTOVSCROLL = 0x0040 + ES_AUTOHSCROLL = 0x0080 + ES_NOHIDESEL = 0x0100 + ES_OEMCONVERT = 0x0400 + ES_READONLY = 0x0800 + ES_WANTRETURN = 0x1000 + ES_NUMBER = 0x2000 +) + +// Edit notifications +const ( + EN_SETFOCUS = 0x0100 + EN_KILLFOCUS = 0x0200 + EN_CHANGE = 0x0300 + EN_UPDATE = 0x0400 + EN_ERRSPACE = 0x0500 + EN_MAXTEXT = 0x0501 + EN_HSCROLL = 0x0601 + EN_VSCROLL = 0x0602 + EN_ALIGN_LTR_EC = 0x0700 + EN_ALIGN_RTL_EC = 0x0701 +) + +// Edit messages +const ( + EM_GETSEL = 0x00B0 + EM_SETSEL = 0x00B1 + EM_GETRECT = 0x00B2 + EM_SETRECT = 0x00B3 + EM_SETRECTNP = 0x00B4 + EM_SCROLL = 0x00B5 + EM_LINESCROLL = 0x00B6 + EM_SCROLLCARET = 0x00B7 + EM_GETMODIFY = 0x00B8 + EM_SETMODIFY = 0x00B9 + EM_GETLINECOUNT = 0x00BA + EM_LINEINDEX = 0x00BB + EM_SETHANDLE = 0x00BC + EM_GETHANDLE = 0x00BD + EM_GETTHUMB = 0x00BE + EM_LINELENGTH = 0x00C1 + EM_REPLACESEL = 0x00C2 + EM_GETLINE = 0x00C4 + EM_LIMITTEXT = 0x00C5 + EM_CANUNDO = 0x00C6 + EM_UNDO = 0x00C7 + EM_FMTLINES = 0x00C8 + EM_LINEFROMCHAR = 0x00C9 + EM_SETTABSTOPS = 0x00CB + EM_SETPASSWORDCHAR = 0x00CC + EM_EMPTYUNDOBUFFER = 0x00CD + EM_GETFIRSTVISIBLELINE = 0x00CE + EM_SETREADONLY = 0x00CF + EM_SETWORDBREAKPROC = 0x00D0 + EM_GETWORDBREAKPROC = 0x00D1 + EM_GETPASSWORDCHAR = 0x00D2 + EM_SETMARGINS = 0x00D3 + EM_GETMARGINS = 0x00D4 + EM_SETLIMITTEXT = EM_LIMITTEXT + EM_GETLIMITTEXT = 0x00D5 + EM_POSFROMCHAR = 0x00D6 + EM_CHARFROMPOS = 0x00D7 + EM_SETIMESTATUS = 0x00D8 + EM_GETIMESTATUS = 0x00D9 + EM_SETCUEBANNER = 0x1501 + EM_GETCUEBANNER = 0x1502 +) + +const ( + CCM_FIRST = 0x2000 + CCM_LAST = CCM_FIRST + 0x200 + CCM_SETBKCOLOR = 8193 + CCM_SETCOLORSCHEME = 8194 + CCM_GETCOLORSCHEME = 8195 + CCM_GETDROPTARGET = 8196 + CCM_SETUNICODEFORMAT = 8197 + CCM_GETUNICODEFORMAT = 8198 + CCM_SETVERSION = 0x2007 + CCM_GETVERSION = 0x2008 + CCM_SETNOTIFYWINDOW = 0x2009 + CCM_SETWINDOWTHEME = 0x200b + CCM_DPISCALE = 0x200c +) + +// Common controls styles +const ( + CCS_TOP = 1 + CCS_NOMOVEY = 2 + CCS_BOTTOM = 3 + CCS_NORESIZE = 4 + CCS_NOPARENTALIGN = 8 + CCS_ADJUSTABLE = 32 + CCS_NODIVIDER = 64 + CCS_VERT = 128 + CCS_LEFT = 129 + CCS_NOMOVEX = 130 + CCS_RIGHT = 131 +) + +// ProgressBar messages +const ( + PROGRESS_CLASS = "msctls_progress32" + PBM_SETPOS = WM_USER + 2 + PBM_DELTAPOS = WM_USER + 3 + PBM_SETSTEP = WM_USER + 4 + PBM_STEPIT = WM_USER + 5 + PBM_SETRANGE32 = 1030 + PBM_GETRANGE = 1031 + PBM_GETPOS = 1032 + PBM_SETBARCOLOR = 1033 + PBM_SETBKCOLOR = CCM_SETBKCOLOR + PBS_SMOOTH = 1 + PBS_VERTICAL = 4 +) + +// Trackbar messages and constants +const ( + TBS_AUTOTICKS = 1 + TBS_VERT = 2 + TBS_HORZ = 0 + TBS_TOP = 4 + TBS_BOTTOM = 0 + TBS_LEFT = 4 + TBS_RIGHT = 0 + TBS_BOTH = 8 + TBS_NOTICKS = 16 + TBS_ENABLESELRANGE = 32 + TBS_FIXEDLENGTH = 64 + TBS_NOTHUMB = 128 + TBS_TOOLTIPS = 0x0100 +) + +const ( + TBM_GETPOS = (WM_USER) + TBM_GETRANGEMIN = (WM_USER + 1) + TBM_GETRANGEMAX = (WM_USER + 2) + TBM_GETTIC = (WM_USER + 3) + TBM_SETTIC = (WM_USER + 4) + TBM_SETPOS = (WM_USER + 5) + TBM_SETRANGE = (WM_USER + 6) + TBM_SETRANGEMIN = (WM_USER + 7) + TBM_SETRANGEMAX = (WM_USER + 8) + TBM_CLEARTICS = (WM_USER + 9) + TBM_SETSEL = (WM_USER + 10) + TBM_SETSELSTART = (WM_USER + 11) + TBM_SETSELEND = (WM_USER + 12) + TBM_GETPTICS = (WM_USER + 14) + TBM_GETTICPOS = (WM_USER + 15) + TBM_GETNUMTICS = (WM_USER + 16) + TBM_GETSELSTART = (WM_USER + 17) + TBM_GETSELEND = (WM_USER + 18) + TBM_CLEARSEL = (WM_USER + 19) + TBM_SETTICFREQ = (WM_USER + 20) + TBM_SETPAGESIZE = (WM_USER + 21) + TBM_GETPAGESIZE = (WM_USER + 22) + TBM_SETLINESIZE = (WM_USER + 23) + TBM_GETLINESIZE = (WM_USER + 24) + TBM_GETTHUMBRECT = (WM_USER + 25) + TBM_GETCHANNELRECT = (WM_USER + 26) + TBM_SETTHUMBLENGTH = (WM_USER + 27) + TBM_GETTHUMBLENGTH = (WM_USER + 28) + TBM_SETTOOLTIPS = (WM_USER + 29) + TBM_GETTOOLTIPS = (WM_USER + 30) + TBM_SETTIPSIDE = (WM_USER + 31) + TBM_SETBUDDY = (WM_USER + 32) + TBM_GETBUDDY = (WM_USER + 33) +) + +const ( + TB_LINEUP = 0 + TB_LINEDOWN = 1 + TB_PAGEUP = 2 + TB_PAGEDOWN = 3 + TB_THUMBPOSITION = 4 + TB_THUMBTRACK = 5 + TB_TOP = 6 + TB_BOTTOM = 7 + TB_ENDTRACK = 8 +) + +// GetOpenFileName and GetSaveFileName extended flags +const ( + OFN_EX_NOPLACESBAR = 0x00000001 +) + +// GetOpenFileName and GetSaveFileName flags +const ( + OFN_ALLOWMULTISELECT = 0x00000200 + OFN_CREATEPROMPT = 0x00002000 + OFN_DONTADDTORECENT = 0x02000000 + OFN_ENABLEHOOK = 0x00000020 + OFN_ENABLEINCLUDENOTIFY = 0x00400000 + OFN_ENABLESIZING = 0x00800000 + OFN_ENABLETEMPLATE = 0x00000040 + OFN_ENABLETEMPLATEHANDLE = 0x00000080 + OFN_EXPLORER = 0x00080000 + OFN_EXTENSIONDIFFERENT = 0x00000400 + OFN_FILEMUSTEXIST = 0x00001000 + OFN_FORCESHOWHIDDEN = 0x10000000 + OFN_HIDEREADONLY = 0x00000004 + OFN_LONGNAMES = 0x00200000 + OFN_NOCHANGEDIR = 0x00000008 + OFN_NODEREFERENCELINKS = 0x00100000 + OFN_NOLONGNAMES = 0x00040000 + OFN_NONETWORKBUTTON = 0x00020000 + OFN_NOREADONLYRETURN = 0x00008000 + OFN_NOTESTFILECREATE = 0x00010000 + OFN_NOVALIDATE = 0x00000100 + OFN_OVERWRITEPROMPT = 0x00000002 + OFN_PATHMUSTEXIST = 0x00000800 + OFN_READONLY = 0x00000001 + OFN_SHAREAWARE = 0x00004000 + OFN_SHOWHELP = 0x00000010 +) + +//SHBrowseForFolder flags +const ( + BIF_RETURNONLYFSDIRS = 0x00000001 + BIF_DONTGOBELOWDOMAIN = 0x00000002 + BIF_STATUSTEXT = 0x00000004 + BIF_RETURNFSANCESTORS = 0x00000008 + BIF_EDITBOX = 0x00000010 + BIF_VALIDATE = 0x00000020 + BIF_NEWDIALOGSTYLE = 0x00000040 + BIF_BROWSEINCLUDEURLS = 0x00000080 + BIF_USENEWUI = BIF_EDITBOX | BIF_NEWDIALOGSTYLE + BIF_UAHINT = 0x00000100 + BIF_NONEWFOLDERBUTTON = 0x00000200 + BIF_NOTRANSLATETARGETS = 0x00000400 + BIF_BROWSEFORCOMPUTER = 0x00001000 + BIF_BROWSEFORPRINTER = 0x00002000 + BIF_BROWSEINCLUDEFILES = 0x00004000 + BIF_SHAREABLE = 0x00008000 + BIF_BROWSEFILEJUNCTIONS = 0x00010000 +) + +//MessageBox flags +const ( + MB_OK = 0x00000000 + MB_OKCANCEL = 0x00000001 + MB_ABORTRETRYIGNORE = 0x00000002 + MB_YESNOCANCEL = 0x00000003 + MB_YESNO = 0x00000004 + MB_RETRYCANCEL = 0x00000005 + MB_CANCELTRYCONTINUE = 0x00000006 + MB_ICONHAND = 0x00000010 + MB_ICONQUESTION = 0x00000020 + MB_ICONEXCLAMATION = 0x00000030 + MB_ICONASTERISK = 0x00000040 + MB_USERICON = 0x00000080 + MB_ICONWARNING = MB_ICONEXCLAMATION + MB_ICONERROR = MB_ICONHAND + MB_ICONINFORMATION = MB_ICONASTERISK + MB_ICONSTOP = MB_ICONHAND + MB_DEFBUTTON1 = 0x00000000 + MB_DEFBUTTON2 = 0x00000100 + MB_DEFBUTTON3 = 0x00000200 + MB_DEFBUTTON4 = 0x00000300 +) + +//COM +const ( + E_INVALIDARG = 0x80070057 + E_OUTOFMEMORY = 0x8007000E + E_UNEXPECTED = 0x8000FFFF +) + +const ( + S_OK = 0 + S_FALSE = 0x0001 + RPC_E_CHANGED_MODE = 0x80010106 +) + +// GetSystemMetrics constants +const ( + SM_CXSCREEN = 0 + SM_CYSCREEN = 1 + SM_CXVSCROLL = 2 + SM_CYHSCROLL = 3 + SM_CYCAPTION = 4 + SM_CXBORDER = 5 + SM_CYBORDER = 6 + SM_CXDLGFRAME = 7 + SM_CYDLGFRAME = 8 + SM_CYVTHUMB = 9 + SM_CXHTHUMB = 10 + SM_CXICON = 11 + SM_CYICON = 12 + SM_CXCURSOR = 13 + SM_CYCURSOR = 14 + SM_CYMENU = 15 + SM_CXFULLSCREEN = 16 + SM_CYFULLSCREEN = 17 + SM_CYKANJIWINDOW = 18 + SM_MOUSEPRESENT = 19 + SM_CYVSCROLL = 20 + SM_CXHSCROLL = 21 + SM_DEBUG = 22 + SM_SWAPBUTTON = 23 + SM_RESERVED1 = 24 + SM_RESERVED2 = 25 + SM_RESERVED3 = 26 + SM_RESERVED4 = 27 + SM_CXMIN = 28 + SM_CYMIN = 29 + SM_CXSIZE = 30 + SM_CYSIZE = 31 + SM_CXFRAME = 32 + SM_CYFRAME = 33 + SM_CXMINTRACK = 34 + SM_CYMINTRACK = 35 + SM_CXDOUBLECLK = 36 + SM_CYDOUBLECLK = 37 + SM_CXICONSPACING = 38 + SM_CYICONSPACING = 39 + SM_MENUDROPALIGNMENT = 40 + SM_PENWINDOWS = 41 + SM_DBCSENABLED = 42 + SM_CMOUSEBUTTONS = 43 + SM_CXFIXEDFRAME = SM_CXDLGFRAME + SM_CYFIXEDFRAME = SM_CYDLGFRAME + SM_CXSIZEFRAME = SM_CXFRAME + SM_CYSIZEFRAME = SM_CYFRAME + SM_SECURE = 44 + SM_CXEDGE = 45 + SM_CYEDGE = 46 + SM_CXMINSPACING = 47 + SM_CYMINSPACING = 48 + SM_CXSMICON = 49 + SM_CYSMICON = 50 + SM_CYSMCAPTION = 51 + SM_CXSMSIZE = 52 + SM_CYSMSIZE = 53 + SM_CXMENUSIZE = 54 + SM_CYMENUSIZE = 55 + SM_ARRANGE = 56 + SM_CXMINIMIZED = 57 + SM_CYMINIMIZED = 58 + SM_CXMAXTRACK = 59 + SM_CYMAXTRACK = 60 + SM_CXMAXIMIZED = 61 + SM_CYMAXIMIZED = 62 + SM_NETWORK = 63 + SM_CLEANBOOT = 67 + SM_CXDRAG = 68 + SM_CYDRAG = 69 + SM_SHOWSOUNDS = 70 + SM_CXMENUCHECK = 71 + SM_CYMENUCHECK = 72 + SM_SLOWMACHINE = 73 + SM_MIDEASTENABLED = 74 + SM_MOUSEWHEELPRESENT = 75 + SM_XVIRTUALSCREEN = 76 + SM_YVIRTUALSCREEN = 77 + SM_CXVIRTUALSCREEN = 78 + SM_CYVIRTUALSCREEN = 79 + SM_CMONITORS = 80 + SM_SAMEDISPLAYFORMAT = 81 + SM_IMMENABLED = 82 + SM_CXFOCUSBORDER = 83 + SM_CYFOCUSBORDER = 84 + SM_TABLETPC = 86 + SM_MEDIACENTER = 87 + SM_STARTER = 88 + SM_SERVERR2 = 89 + SM_CMETRICS = 91 + SM_REMOTESESSION = 0x1000 + SM_SHUTTINGDOWN = 0x2000 + SM_REMOTECONTROL = 0x2001 + SM_CARETBLINKINGENABLED = 0x2002 +) + +const ( + CLSCTX_INPROC_SERVER = 1 + CLSCTX_INPROC_HANDLER = 2 + CLSCTX_LOCAL_SERVER = 4 + CLSCTX_INPROC_SERVER16 = 8 + CLSCTX_REMOTE_SERVER = 16 + CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER + CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER + CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER +) + +const ( + COINIT_APARTMENTTHREADED = 0x2 + COINIT_MULTITHREADED = 0x0 + COINIT_DISABLE_OLE1DDE = 0x4 + COINIT_SPEED_OVER_MEMORY = 0x8 +) + +const ( + DISPATCH_METHOD = 1 + DISPATCH_PROPERTYGET = 2 + DISPATCH_PROPERTYPUT = 4 + DISPATCH_PROPERTYPUTREF = 8 +) + +const ( + CC_FASTCALL = iota + CC_CDECL + CC_MSCPASCAL + CC_PASCAL = CC_MSCPASCAL + CC_MACPASCAL + CC_STDCALL + CC_FPFASTCALL + CC_SYSCALL + CC_MPWCDECL + CC_MPWPASCAL + CC_MAX = CC_MPWPASCAL +) + +const ( + VT_EMPTY = 0x0 + VT_NULL = 0x1 + VT_I2 = 0x2 + VT_I4 = 0x3 + VT_R4 = 0x4 + VT_R8 = 0x5 + VT_CY = 0x6 + VT_DATE = 0x7 + VT_BSTR = 0x8 + VT_DISPATCH = 0x9 + VT_ERROR = 0xa + VT_BOOL = 0xb + VT_VARIANT = 0xc + VT_UNKNOWN = 0xd + VT_DECIMAL = 0xe + VT_I1 = 0x10 + VT_UI1 = 0x11 + VT_UI2 = 0x12 + VT_UI4 = 0x13 + VT_I8 = 0x14 + VT_UI8 = 0x15 + VT_INT = 0x16 + VT_UINT = 0x17 + VT_VOID = 0x18 + VT_HRESULT = 0x19 + VT_PTR = 0x1a + VT_SAFEARRAY = 0x1b + VT_CARRAY = 0x1c + VT_USERDEFINED = 0x1d + VT_LPSTR = 0x1e + VT_LPWSTR = 0x1f + VT_RECORD = 0x24 + VT_INT_PTR = 0x25 + VT_UINT_PTR = 0x26 + VT_FILETIME = 0x40 + VT_BLOB = 0x41 + VT_STREAM = 0x42 + VT_STORAGE = 0x43 + VT_STREAMED_OBJECT = 0x44 + VT_STORED_OBJECT = 0x45 + VT_BLOB_OBJECT = 0x46 + VT_CF = 0x47 + VT_CLSID = 0x48 + VT_BSTR_BLOB = 0xfff + VT_VECTOR = 0x1000 + VT_ARRAY = 0x2000 + VT_BYREF = 0x4000 + VT_RESERVED = 0x8000 + VT_ILLEGAL = 0xffff + VT_ILLEGALMASKED = 0xfff + VT_TYPEMASK = 0xfff +) + +const ( + DISPID_UNKNOWN = -1 + DISPID_VALUE = 0 + DISPID_PROPERTYPUT = -3 + DISPID_NEWENUM = -4 + DISPID_EVALUATE = -5 + DISPID_CONSTRUCTOR = -6 + DISPID_DESTRUCTOR = -7 + DISPID_COLLECT = -8 +) + +const ( + MONITOR_DEFAULTTONULL = 0x00000000 + MONITOR_DEFAULTTOPRIMARY = 0x00000001 + MONITOR_DEFAULTTONEAREST = 0x00000002 + + MONITORINFOF_PRIMARY = 0x00000001 +) + +const ( + CCHDEVICENAME = 32 + CCHFORMNAME = 32 +) + +const ( + IDOK = 1 + IDCANCEL = 2 + IDABORT = 3 + IDRETRY = 4 + IDIGNORE = 5 + IDYES = 6 + IDNO = 7 + IDCLOSE = 8 + IDHELP = 9 + IDTRYAGAIN = 10 + IDCONTINUE = 11 + IDTIMEOUT = 32000 +) + +// Generic WM_NOTIFY notification codes +const ( + NM_FIRST = 0 + NM_OUTOFMEMORY = NM_FIRST - 1 + NM_CLICK = NM_FIRST - 2 + NM_DBLCLK = NM_FIRST - 3 + NM_RETURN = NM_FIRST - 4 + NM_RCLICK = NM_FIRST - 5 + NM_RDBLCLK = NM_FIRST - 6 + NM_SETFOCUS = NM_FIRST - 7 + NM_KILLFOCUS = NM_FIRST - 8 + NM_CUSTOMDRAW = NM_FIRST - 12 + NM_HOVER = NM_FIRST - 13 + NM_NCHITTEST = NM_FIRST - 14 + NM_KEYDOWN = NM_FIRST - 15 + NM_RELEASEDCAPTURE = NM_FIRST - 16 + NM_SETCURSOR = NM_FIRST - 17 + NM_CHAR = NM_FIRST - 18 + NM_TOOLTIPSCREATED = NM_FIRST - 19 + NM_LAST = NM_FIRST - 99 +) + +// ListView messages +const ( + LVM_FIRST = 0x1000 + LVM_GETITEMCOUNT = LVM_FIRST + 4 + LVM_SETIMAGELIST = LVM_FIRST + 3 + LVM_GETIMAGELIST = LVM_FIRST + 2 + LVM_GETITEM = LVM_FIRST + 75 + LVM_SETITEM = LVM_FIRST + 76 + LVM_INSERTITEM = LVM_FIRST + 77 + LVM_DELETEITEM = LVM_FIRST + 8 + LVM_DELETEALLITEMS = LVM_FIRST + 9 + LVM_GETCALLBACKMASK = LVM_FIRST + 10 + LVM_SETCALLBACKMASK = LVM_FIRST + 11 + LVM_SETUNICODEFORMAT = CCM_SETUNICODEFORMAT + LVM_GETNEXTITEM = LVM_FIRST + 12 + LVM_FINDITEM = LVM_FIRST + 83 + LVM_GETITEMRECT = LVM_FIRST + 14 + LVM_GETSTRINGWIDTH = LVM_FIRST + 87 + LVM_HITTEST = LVM_FIRST + 18 + LVM_ENSUREVISIBLE = LVM_FIRST + 19 + LVM_SCROLL = LVM_FIRST + 20 + LVM_REDRAWITEMS = LVM_FIRST + 21 + LVM_ARRANGE = LVM_FIRST + 22 + LVM_EDITLABEL = LVM_FIRST + 118 + LVM_GETEDITCONTROL = LVM_FIRST + 24 + LVM_GETCOLUMN = LVM_FIRST + 95 + LVM_SETCOLUMN = LVM_FIRST + 96 + LVM_INSERTCOLUMN = LVM_FIRST + 97 + LVM_DELETECOLUMN = LVM_FIRST + 28 + LVM_GETCOLUMNWIDTH = LVM_FIRST + 29 + LVM_SETCOLUMNWIDTH = LVM_FIRST + 30 + LVM_GETHEADER = LVM_FIRST + 31 + LVM_CREATEDRAGIMAGE = LVM_FIRST + 33 + LVM_GETVIEWRECT = LVM_FIRST + 34 + LVM_GETTEXTCOLOR = LVM_FIRST + 35 + LVM_SETTEXTCOLOR = LVM_FIRST + 36 + LVM_GETTEXTBKCOLOR = LVM_FIRST + 37 + LVM_SETTEXTBKCOLOR = LVM_FIRST + 38 + LVM_GETTOPINDEX = LVM_FIRST + 39 + LVM_GETCOUNTPERPAGE = LVM_FIRST + 40 + LVM_GETORIGIN = LVM_FIRST + 41 + LVM_UPDATE = LVM_FIRST + 42 + LVM_SETITEMSTATE = LVM_FIRST + 43 + LVM_GETITEMSTATE = LVM_FIRST + 44 + LVM_GETITEMTEXT = LVM_FIRST + 115 + LVM_SETITEMTEXT = LVM_FIRST + 116 + LVM_SETITEMCOUNT = LVM_FIRST + 47 + LVM_SORTITEMS = LVM_FIRST + 48 + LVM_SETITEMPOSITION32 = LVM_FIRST + 49 + LVM_GETSELECTEDCOUNT = LVM_FIRST + 50 + LVM_GETITEMSPACING = LVM_FIRST + 51 + LVM_GETISEARCHSTRING = LVM_FIRST + 117 + LVM_SETICONSPACING = LVM_FIRST + 53 + LVM_SETEXTENDEDLISTVIEWSTYLE = LVM_FIRST + 54 + LVM_GETEXTENDEDLISTVIEWSTYLE = LVM_FIRST + 55 + LVM_GETSUBITEMRECT = LVM_FIRST + 56 + LVM_SUBITEMHITTEST = LVM_FIRST + 57 + LVM_SETCOLUMNORDERARRAY = LVM_FIRST + 58 + LVM_GETCOLUMNORDERARRAY = LVM_FIRST + 59 + LVM_SETHOTITEM = LVM_FIRST + 60 + LVM_GETHOTITEM = LVM_FIRST + 61 + LVM_SETHOTCURSOR = LVM_FIRST + 62 + LVM_GETHOTCURSOR = LVM_FIRST + 63 + LVM_APPROXIMATEVIEWRECT = LVM_FIRST + 64 + LVM_SETWORKAREAS = LVM_FIRST + 65 + LVM_GETWORKAREAS = LVM_FIRST + 70 + LVM_GETNUMBEROFWORKAREAS = LVM_FIRST + 73 + LVM_GETSELECTIONMARK = LVM_FIRST + 66 + LVM_SETSELECTIONMARK = LVM_FIRST + 67 + LVM_SETHOVERTIME = LVM_FIRST + 71 + LVM_GETHOVERTIME = LVM_FIRST + 72 + LVM_SETTOOLTIPS = LVM_FIRST + 74 + LVM_GETTOOLTIPS = LVM_FIRST + 78 + LVM_SORTITEMSEX = LVM_FIRST + 81 + LVM_SETBKIMAGE = LVM_FIRST + 138 + LVM_GETBKIMAGE = LVM_FIRST + 139 + LVM_SETSELECTEDCOLUMN = LVM_FIRST + 140 + LVM_SETVIEW = LVM_FIRST + 142 + LVM_GETVIEW = LVM_FIRST + 143 + LVM_INSERTGROUP = LVM_FIRST + 145 + LVM_SETGROUPINFO = LVM_FIRST + 147 + LVM_GETGROUPINFO = LVM_FIRST + 149 + LVM_REMOVEGROUP = LVM_FIRST + 150 + LVM_MOVEGROUP = LVM_FIRST + 151 + LVM_GETGROUPCOUNT = LVM_FIRST + 152 + LVM_GETGROUPINFOBYINDEX = LVM_FIRST + 153 + LVM_MOVEITEMTOGROUP = LVM_FIRST + 154 + LVM_GETGROUPRECT = LVM_FIRST + 98 + LVM_SETGROUPMETRICS = LVM_FIRST + 155 + LVM_GETGROUPMETRICS = LVM_FIRST + 156 + LVM_ENABLEGROUPVIEW = LVM_FIRST + 157 + LVM_SORTGROUPS = LVM_FIRST + 158 + LVM_INSERTGROUPSORTED = LVM_FIRST + 159 + LVM_REMOVEALLGROUPS = LVM_FIRST + 160 + LVM_HASGROUP = LVM_FIRST + 161 + LVM_GETGROUPSTATE = LVM_FIRST + 92 + LVM_GETFOCUSEDGROUP = LVM_FIRST + 93 + LVM_SETTILEVIEWINFO = LVM_FIRST + 162 + LVM_GETTILEVIEWINFO = LVM_FIRST + 163 + LVM_SETTILEINFO = LVM_FIRST + 164 + LVM_GETTILEINFO = LVM_FIRST + 165 + LVM_SETINSERTMARK = LVM_FIRST + 166 + LVM_GETINSERTMARK = LVM_FIRST + 167 + LVM_INSERTMARKHITTEST = LVM_FIRST + 168 + LVM_GETINSERTMARKRECT = LVM_FIRST + 169 + LVM_SETINSERTMARKCOLOR = LVM_FIRST + 170 + LVM_GETINSERTMARKCOLOR = LVM_FIRST + 171 + LVM_SETINFOTIP = LVM_FIRST + 173 + LVM_GETSELECTEDCOLUMN = LVM_FIRST + 174 + LVM_ISGROUPVIEWENABLED = LVM_FIRST + 175 + LVM_GETOUTLINECOLOR = LVM_FIRST + 176 + LVM_SETOUTLINECOLOR = LVM_FIRST + 177 + LVM_CANCELEDITLABEL = LVM_FIRST + 179 + LVM_MAPINDEXTOID = LVM_FIRST + 180 + LVM_MAPIDTOINDEX = LVM_FIRST + 181 + LVM_ISITEMVISIBLE = LVM_FIRST + 182 + LVM_GETNEXTITEMINDEX = LVM_FIRST + 211 +) + +// ListView notifications +const ( + LVN_FIRST = -100 + + LVN_ITEMCHANGING = LVN_FIRST - 0 + LVN_ITEMCHANGED = LVN_FIRST - 1 + LVN_INSERTITEM = LVN_FIRST - 2 + LVN_DELETEITEM = LVN_FIRST - 3 + LVN_DELETEALLITEMS = LVN_FIRST - 4 + LVN_BEGINLABELEDITA = LVN_FIRST - 5 + LVN_BEGINLABELEDITW = LVN_FIRST - 75 + LVN_ENDLABELEDITA = LVN_FIRST - 6 + LVN_ENDLABELEDITW = LVN_FIRST - 76 + LVN_COLUMNCLICK = LVN_FIRST - 8 + LVN_BEGINDRAG = LVN_FIRST - 9 + LVN_BEGINRDRAG = LVN_FIRST - 11 + LVN_ODCACHEHINT = LVN_FIRST - 13 + LVN_ODFINDITEMA = LVN_FIRST - 52 + LVN_ODFINDITEMW = LVN_FIRST - 79 + LVN_ITEMACTIVATE = LVN_FIRST - 14 + LVN_ODSTATECHANGED = LVN_FIRST - 15 + LVN_HOTTRACK = LVN_FIRST - 21 + LVN_GETDISPINFO = LVN_FIRST - 77 + LVN_SETDISPINFO = LVN_FIRST - 78 + LVN_KEYDOWN = LVN_FIRST - 55 + LVN_MARQUEEBEGIN = LVN_FIRST - 56 + LVN_GETINFOTIP = LVN_FIRST - 58 + LVN_INCREMENTALSEARCH = LVN_FIRST - 63 + LVN_BEGINSCROLL = LVN_FIRST - 80 + LVN_ENDSCROLL = LVN_FIRST - 81 +) + +const ( + LVSCW_AUTOSIZE = ^uintptr(0) + LVSCW_AUTOSIZE_USEHEADER = ^uintptr(1) +) + +// ListView LVNI constants +const ( + LVNI_ALL = 0 + LVNI_FOCUSED = 1 + LVNI_SELECTED = 2 + LVNI_CUT = 4 + LVNI_DROPHILITED = 8 + LVNI_ABOVE = 256 + LVNI_BELOW = 512 + LVNI_TOLEFT = 1024 + LVNI_TORIGHT = 2048 +) + +// ListView styles +const ( + LVS_ICON = 0x0000 + LVS_REPORT = 0x0001 + LVS_SMALLICON = 0x0002 + LVS_LIST = 0x0003 + LVS_TYPEMASK = 0x0003 + LVS_SINGLESEL = 0x0004 + LVS_SHOWSELALWAYS = 0x0008 + LVS_SORTASCENDING = 0x0010 + LVS_SORTDESCENDING = 0x0020 + LVS_SHAREIMAGELISTS = 0x0040 + LVS_NOLABELWRAP = 0x0080 + LVS_AUTOARRANGE = 0x0100 + LVS_EDITLABELS = 0x0200 + LVS_OWNERDATA = 0x1000 + LVS_NOSCROLL = 0x2000 + LVS_TYPESTYLEMASK = 0xfc00 + LVS_ALIGNTOP = 0x0000 + LVS_ALIGNLEFT = 0x0800 + LVS_ALIGNMASK = 0x0c00 + LVS_OWNERDRAWFIXED = 0x0400 + LVS_NOCOLUMNHEADER = 0x4000 + LVS_NOSORTHEADER = 0x8000 +) + +// ListView extended styles +const ( + LVS_EX_GRIDLINES = 0x00000001 + LVS_EX_SUBITEMIMAGES = 0x00000002 + LVS_EX_CHECKBOXES = 0x00000004 + LVS_EX_TRACKSELECT = 0x00000008 + LVS_EX_HEADERDRAGDROP = 0x00000010 + LVS_EX_FULLROWSELECT = 0x00000020 + LVS_EX_ONECLICKACTIVATE = 0x00000040 + LVS_EX_TWOCLICKACTIVATE = 0x00000080 + LVS_EX_FLATSB = 0x00000100 + LVS_EX_REGIONAL = 0x00000200 + LVS_EX_INFOTIP = 0x00000400 + LVS_EX_UNDERLINEHOT = 0x00000800 + LVS_EX_UNDERLINECOLD = 0x00001000 + LVS_EX_MULTIWORKAREAS = 0x00002000 + LVS_EX_LABELTIP = 0x00004000 + LVS_EX_BORDERSELECT = 0x00008000 + LVS_EX_DOUBLEBUFFER = 0x00010000 + LVS_EX_HIDELABELS = 0x00020000 + LVS_EX_SINGLEROW = 0x00040000 + LVS_EX_SNAPTOGRID = 0x00080000 + LVS_EX_SIMPLESELECT = 0x00100000 +) + +// ListView column flags +const ( + LVCF_FMT = 0x0001 + LVCF_WIDTH = 0x0002 + LVCF_TEXT = 0x0004 + LVCF_SUBITEM = 0x0008 + LVCF_IMAGE = 0x0010 + LVCF_ORDER = 0x0020 +) + +// ListView column format constants +const ( + LVCFMT_LEFT = 0x0000 + LVCFMT_RIGHT = 0x0001 + LVCFMT_CENTER = 0x0002 + LVCFMT_JUSTIFYMASK = 0x0003 + LVCFMT_IMAGE = 0x0800 + LVCFMT_BITMAP_ON_RIGHT = 0x1000 + LVCFMT_COL_HAS_IMAGES = 0x8000 +) + +// ListView item flags +const ( + LVIF_TEXT = 0x00000001 + LVIF_IMAGE = 0x00000002 + LVIF_PARAM = 0x00000004 + LVIF_STATE = 0x00000008 + LVIF_INDENT = 0x00000010 + LVIF_NORECOMPUTE = 0x00000800 + LVIF_GROUPID = 0x00000100 + LVIF_COLUMNS = 0x00000200 +) + +const LVFI_PARAM = 0x0001 + +// ListView item states +const ( + LVIS_FOCUSED = 1 + LVIS_SELECTED = 2 + LVIS_CUT = 4 + LVIS_DROPHILITED = 8 + LVIS_OVERLAYMASK = 0xF00 + LVIS_STATEIMAGEMASK = 0xF000 +) + +// ListView hit test constants +const ( + LVHT_NOWHERE = 0x00000001 + LVHT_ONITEMICON = 0x00000002 + LVHT_ONITEMLABEL = 0x00000004 + LVHT_ONITEMSTATEICON = 0x00000008 + LVHT_ONITEM = LVHT_ONITEMICON | LVHT_ONITEMLABEL | LVHT_ONITEMSTATEICON + + LVHT_ABOVE = 0x00000008 + LVHT_BELOW = 0x00000010 + LVHT_TORIGHT = 0x00000020 + LVHT_TOLEFT = 0x00000040 +) + +// ListView image list types +const ( + LVSIL_NORMAL = 0 + LVSIL_SMALL = 1 + LVSIL_STATE = 2 + LVSIL_GROUPHEADER = 3 +) + +// InitCommonControlsEx flags +const ( + ICC_LISTVIEW_CLASSES = 1 + ICC_TREEVIEW_CLASSES = 2 + ICC_BAR_CLASSES = 4 + ICC_TAB_CLASSES = 8 + ICC_UPDOWN_CLASS = 16 + ICC_PROGRESS_CLASS = 32 + ICC_HOTKEY_CLASS = 64 + ICC_ANIMATE_CLASS = 128 + ICC_WIN95_CLASSES = 255 + ICC_DATE_CLASSES = 256 + ICC_USEREX_CLASSES = 512 + ICC_COOL_CLASSES = 1024 + ICC_INTERNET_CLASSES = 2048 + ICC_PAGESCROLLER_CLASS = 4096 + ICC_NATIVEFNTCTL_CLASS = 8192 + INFOTIPSIZE = 1024 + ICC_STANDARD_CLASSES = 0x00004000 + ICC_LINK_CLASS = 0x00008000 +) + +// Dialog Codes +const ( + DLGC_WANTARROWS = 0x0001 + DLGC_WANTTAB = 0x0002 + DLGC_WANTALLKEYS = 0x0004 + DLGC_WANTMESSAGE = 0x0004 + DLGC_HASSETSEL = 0x0008 + DLGC_DEFPUSHBUTTON = 0x0010 + DLGC_UNDEFPUSHBUTTON = 0x0020 + DLGC_RADIOBUTTON = 0x0040 + DLGC_WANTCHARS = 0x0080 + DLGC_STATIC = 0x0100 + DLGC_BUTTON = 0x2000 +) + +// Get/SetWindowWord/Long offsets for use with WC_DIALOG windows +const ( + DWL_MSGRESULT = 0 + DWL_DLGPROC = 4 + DWL_USER = 8 +) + +// Registry predefined keys +const ( + HKEY_CLASSES_ROOT HKEY = 0x80000000 + HKEY_CURRENT_USER HKEY = 0x80000001 + HKEY_LOCAL_MACHINE HKEY = 0x80000002 + HKEY_USERS HKEY = 0x80000003 + HKEY_PERFORMANCE_DATA HKEY = 0x80000004 + HKEY_CURRENT_CONFIG HKEY = 0x80000005 + HKEY_DYN_DATA HKEY = 0x80000006 +) + +// Registry Key Security and Access Rights +const ( + KEY_ALL_ACCESS = 0xF003F + KEY_CREATE_SUB_KEY = 0x0004 + KEY_ENUMERATE_SUB_KEYS = 0x0008 + KEY_NOTIFY = 0x0010 + KEY_QUERY_VALUE = 0x0001 + KEY_SET_VALUE = 0x0002 + KEY_READ = 0x20019 + KEY_WRITE = 0x20006 +) + +const ( + NFR_ANSI = 1 + NFR_UNICODE = 2 + NF_QUERY = 3 + NF_REQUERY = 4 +) + +// Registry value types +const ( + RRF_RT_REG_NONE = 0x00000001 + RRF_RT_REG_SZ = 0x00000002 + RRF_RT_REG_EXPAND_SZ = 0x00000004 + RRF_RT_REG_BINARY = 0x00000008 + RRF_RT_REG_DWORD = 0x00000010 + RRF_RT_REG_MULTI_SZ = 0x00000020 + RRF_RT_REG_QWORD = 0x00000040 + RRF_RT_DWORD = (RRF_RT_REG_BINARY | RRF_RT_REG_DWORD) + RRF_RT_QWORD = (RRF_RT_REG_BINARY | RRF_RT_REG_QWORD) + RRF_RT_ANY = 0x0000ffff + RRF_NOEXPAND = 0x10000000 + RRF_ZEROONFAILURE = 0x20000000 + REG_PROCESS_APPKEY = 0x00000001 + REG_MUI_STRING_TRUNCATE = 0x00000001 +) + +// PeekMessage wRemoveMsg value +const ( + PM_NOREMOVE = 0x000 + PM_REMOVE = 0x001 + PM_NOYIELD = 0x002 +) + +// ImageList flags +const ( + ILC_MASK = 0x00000001 + ILC_COLOR = 0x00000000 + ILC_COLORDDB = 0x000000FE + ILC_COLOR4 = 0x00000004 + ILC_COLOR8 = 0x00000008 + ILC_COLOR16 = 0x00000010 + ILC_COLOR24 = 0x00000018 + ILC_COLOR32 = 0x00000020 + ILC_PALETTE = 0x00000800 + ILC_MIRROR = 0x00002000 + ILC_PERITEMMIRROR = 0x00008000 + ILC_ORIGINALSIZE = 0x00010000 + ILC_HIGHQUALITYSCALE = 0x00020000 +) + +// Keystroke Message Flags +const ( + KF_EXTENDED = 0x0100 + KF_DLGMODE = 0x0800 + KF_MENUMODE = 0x1000 + KF_ALTDOWN = 0x2000 + KF_REPEAT = 0x4000 + KF_UP = 0x8000 +) + +// Virtual-Key Codes +/* +const ( + VK_LBUTTON = 0x01 + VK_RBUTTON = 0x02 + VK_CANCEL = 0x03 + VK_MBUTTON = 0x04 + VK_XBUTTON1 = 0x05 + VK_XBUTTON2 = 0x06 + VK_BACK = 0x08 + VK_TAB = 0x09 + VK_CLEAR = 0x0C + VK_RETURN = 0x0D + VK_SHIFT = 0x10 + VK_CONTROL = 0x11 + VK_MENU = 0x12 + VK_PAUSE = 0x13 + VK_CAPITAL = 0x14 + VK_KANA = 0x15 + VK_HANGEUL = 0x15 + VK_HANGUL = 0x15 + VK_JUNJA = 0x17 + VK_FINAL = 0x18 + VK_HANJA = 0x19 + VK_KANJI = 0x19 + VK_ESCAPE = 0x1B + VK_CONVERT = 0x1C + VK_NONCONVERT = 0x1D + VK_ACCEPT = 0x1E + VK_MODECHANGE = 0x1F + VK_SPACE = 0x20 + VK_PRIOR = 0x21 + VK_NEXT = 0x22 + VK_END = 0x23 + VK_HOME = 0x24 + VK_LEFT = 0x25 + VK_UP = 0x26 + VK_RIGHT = 0x27 + VK_DOWN = 0x28 + VK_SELECT = 0x29 + VK_PRINT = 0x2A + VK_EXECUTE = 0x2B + VK_SNAPSHOT = 0x2C + VK_INSERT = 0x2D + VK_DELETE = 0x2E + VK_HELP = 0x2F + VK_LWIN = 0x5B + VK_RWIN = 0x5C + VK_APPS = 0x5D + VK_SLEEP = 0x5F + VK_NUMPAD0 = 0x60 + VK_NUMPAD1 = 0x61 + VK_NUMPAD2 = 0x62 + VK_NUMPAD3 = 0x63 + VK_NUMPAD4 = 0x64 + VK_NUMPAD5 = 0x65 + VK_NUMPAD6 = 0x66 + VK_NUMPAD7 = 0x67 + VK_NUMPAD8 = 0x68 + VK_NUMPAD9 = 0x69 + VK_MULTIPLY = 0x6A + VK_ADD = 0x6B + VK_SEPARATOR = 0x6C + VK_SUBTRACT = 0x6D + VK_DECIMAL = 0x6E + VK_DIVIDE = 0x6F + VK_F1 = 0x70 + VK_F2 = 0x71 + VK_F3 = 0x72 + VK_F4 = 0x73 + VK_F5 = 0x74 + VK_F6 = 0x75 + VK_F7 = 0x76 + VK_F8 = 0x77 + VK_F9 = 0x78 + VK_F10 = 0x79 + VK_F11 = 0x7A + VK_F12 = 0x7B + VK_F13 = 0x7C + VK_F14 = 0x7D + VK_F15 = 0x7E + VK_F16 = 0x7F + VK_F17 = 0x80 + VK_F18 = 0x81 + VK_F19 = 0x82 + VK_F20 = 0x83 + VK_F21 = 0x84 + VK_F22 = 0x85 + VK_F23 = 0x86 + VK_F24 = 0x87 + VK_NUMLOCK = 0x90 + VK_SCROLL = 0x91 + VK_OEM_NEC_EQUAL = 0x92 + VK_OEM_FJ_JISHO = 0x92 + VK_OEM_FJ_MASSHOU = 0x93 + VK_OEM_FJ_TOUROKU = 0x94 + VK_OEM_FJ_LOYA = 0x95 + VK_OEM_FJ_ROYA = 0x96 + VK_LSHIFT = 0xA0 + VK_RSHIFT = 0xA1 + VK_LCONTROL = 0xA2 + VK_RCONTROL = 0xA3 + VK_LMENU = 0xA4 + VK_RMENU = 0xA5 + VK_BROWSER_BACK = 0xA6 + VK_BROWSER_FORWARD = 0xA7 + VK_BROWSER_REFRESH = 0xA8 + VK_BROWSER_STOP = 0xA9 + VK_BROWSER_SEARCH = 0xAA + VK_BROWSER_FAVORITES = 0xAB + VK_BROWSER_HOME = 0xAC + VK_VOLUME_MUTE = 0xAD + VK_VOLUME_DOWN = 0xAE + VK_VOLUME_UP = 0xAF + VK_MEDIA_NEXT_TRACK = 0xB0 + VK_MEDIA_PREV_TRACK = 0xB1 + VK_MEDIA_STOP = 0xB2 + VK_MEDIA_PLAY_PAUSE = 0xB3 + VK_LAUNCH_MAIL = 0xB4 + VK_LAUNCH_MEDIA_SELECT = 0xB5 + VK_LAUNCH_APP1 = 0xB6 + VK_LAUNCH_APP2 = 0xB7 + VK_OEM_1 = 0xBA + VK_OEM_PLUS = 0xBB + VK_OEM_COMMA = 0xBC + VK_OEM_MINUS = 0xBD + VK_OEM_PERIOD = 0xBE + VK_OEM_2 = 0xBF + VK_OEM_3 = 0xC0 + VK_OEM_4 = 0xDB + VK_OEM_5 = 0xDC + VK_OEM_6 = 0xDD + VK_OEM_7 = 0xDE + VK_OEM_8 = 0xDF + VK_OEM_AX = 0xE1 + VK_OEM_102 = 0xE2 + VK_ICO_HELP = 0xE3 + VK_ICO_00 = 0xE4 + VK_PROCESSKEY = 0xE5 + VK_ICO_CLEAR = 0xE6 + VK_OEM_RESET = 0xE9 + VK_OEM_JUMP = 0xEA + VK_OEM_PA1 = 0xEB + VK_OEM_PA2 = 0xEC + VK_OEM_PA3 = 0xED + VK_OEM_WSCTRL = 0xEE + VK_OEM_CUSEL = 0xEF + VK_OEM_ATTN = 0xF0 + VK_OEM_FINISH = 0xF1 + VK_OEM_COPY = 0xF2 + VK_OEM_AUTO = 0xF3 + VK_OEM_ENLW = 0xF4 + VK_OEM_BACKTAB = 0xF5 + VK_ATTN = 0xF6 + VK_CRSEL = 0xF7 + VK_EXSEL = 0xF8 + VK_EREOF = 0xF9 + VK_PLAY = 0xFA + VK_ZOOM = 0xFB + VK_NONAME = 0xFC + VK_PA1 = 0xFD + VK_OEM_CLEAR = 0xFE +)*/ + +// Registry Value Types +const ( + REG_NONE = 0 + REG_SZ = 1 + REG_EXPAND_SZ = 2 + REG_BINARY = 3 + REG_DWORD = 4 + REG_DWORD_LITTLE_ENDIAN = 4 + REG_DWORD_BIG_ENDIAN = 5 + REG_LINK = 6 + REG_MULTI_SZ = 7 + REG_RESOURCE_LIST = 8 + REG_FULL_RESOURCE_DESCRIPTOR = 9 + REG_RESOURCE_REQUIREMENTS_LIST = 10 + REG_QWORD = 11 + REG_QWORD_LITTLE_ENDIAN = 11 +) + +// Tooltip styles +const ( + TTS_ALWAYSTIP = 0x01 + TTS_NOPREFIX = 0x02 + TTS_NOANIMATE = 0x10 + TTS_NOFADE = 0x20 + TTS_BALLOON = 0x40 + TTS_CLOSE = 0x80 + TTS_USEVISUALSTYLE = 0x100 +) + +// Tooltip messages +const ( + TTM_ACTIVATE = (WM_USER + 1) + TTM_SETDELAYTIME = (WM_USER + 3) + TTM_ADDTOOL = (WM_USER + 50) + TTM_DELTOOL = (WM_USER + 51) + TTM_NEWTOOLRECT = (WM_USER + 52) + TTM_RELAYEVENT = (WM_USER + 7) + TTM_GETTOOLINFO = (WM_USER + 53) + TTM_SETTOOLINFO = (WM_USER + 54) + TTM_HITTEST = (WM_USER + 55) + TTM_GETTEXT = (WM_USER + 56) + TTM_UPDATETIPTEXT = (WM_USER + 57) + TTM_GETTOOLCOUNT = (WM_USER + 13) + TTM_ENUMTOOLS = (WM_USER + 58) + TTM_GETCURRENTTOOL = (WM_USER + 59) + TTM_WINDOWFROMPOINT = (WM_USER + 16) + TTM_TRACKACTIVATE = (WM_USER + 17) + TTM_TRACKPOSITION = (WM_USER + 18) + TTM_SETTIPBKCOLOR = (WM_USER + 19) + TTM_SETTIPTEXTCOLOR = (WM_USER + 20) + TTM_GETDELAYTIME = (WM_USER + 21) + TTM_GETTIPBKCOLOR = (WM_USER + 22) + TTM_GETTIPTEXTCOLOR = (WM_USER + 23) + TTM_SETMAXTIPWIDTH = (WM_USER + 24) + TTM_GETMAXTIPWIDTH = (WM_USER + 25) + TTM_SETMARGIN = (WM_USER + 26) + TTM_GETMARGIN = (WM_USER + 27) + TTM_POP = (WM_USER + 28) + TTM_UPDATE = (WM_USER + 29) + TTM_GETBUBBLESIZE = (WM_USER + 30) + TTM_ADJUSTRECT = (WM_USER + 31) + TTM_SETTITLE = (WM_USER + 33) + TTM_POPUP = (WM_USER + 34) + TTM_GETTITLE = (WM_USER + 35) +) + +// Tooltip icons +const ( + TTI_NONE = 0 + TTI_INFO = 1 + TTI_WARNING = 2 + TTI_ERROR = 3 + TTI_INFO_LARGE = 4 + TTI_WARNING_LARGE = 5 + TTI_ERROR_LARGE = 6 +) + +// Tooltip notifications +const ( + TTN_FIRST = -520 + TTN_LAST = -549 + TTN_GETDISPINFO = (TTN_FIRST - 10) + TTN_SHOW = (TTN_FIRST - 1) + TTN_POP = (TTN_FIRST - 2) + TTN_LINKCLICK = (TTN_FIRST - 3) + TTN_NEEDTEXT = TTN_GETDISPINFO +) + +const ( + TTF_IDISHWND = 0x0001 + TTF_CENTERTIP = 0x0002 + TTF_RTLREADING = 0x0004 + TTF_SUBCLASS = 0x0010 + TTF_TRACK = 0x0020 + TTF_ABSOLUTE = 0x0080 + TTF_TRANSPARENT = 0x0100 + TTF_PARSELINKS = 0x1000 + TTF_DI_SETITEM = 0x8000 +) + +const ( + SWP_NOSIZE = 0x0001 + SWP_NOMOVE = 0x0002 + SWP_NOZORDER = 0x0004 + SWP_NOREDRAW = 0x0008 + SWP_NOACTIVATE = 0x0010 + SWP_FRAMECHANGED = 0x0020 + SWP_SHOWWINDOW = 0x0040 + SWP_HIDEWINDOW = 0x0080 + SWP_NOCOPYBITS = 0x0100 + SWP_NOOWNERZORDER = 0x0200 + SWP_NOSENDCHANGING = 0x0400 + SWP_DRAWFRAME = SWP_FRAMECHANGED + SWP_NOREPOSITION = SWP_NOOWNERZORDER + SWP_DEFERERASE = 0x2000 + SWP_ASYNCWINDOWPOS = 0x4000 +) + +// Predefined window handles +const ( + HWND_BROADCAST = HWND(0xFFFF) + HWND_BOTTOM = HWND(1) + HWND_NOTOPMOST = ^HWND(1) // -2 + HWND_TOP = HWND(0) + HWND_TOPMOST = ^HWND(0) // -1 + HWND_DESKTOP = HWND(0) + HWND_MESSAGE = ^HWND(2) // -3 +) + +// Pen types +const ( + PS_COSMETIC = 0x00000000 + PS_GEOMETRIC = 0x00010000 + PS_TYPE_MASK = 0x000F0000 +) + +// Pen styles +const ( + PS_SOLID = 0 + PS_DASH = 1 + PS_DOT = 2 + PS_DASHDOT = 3 + PS_DASHDOTDOT = 4 + PS_NULL = 5 + PS_INSIDEFRAME = 6 + PS_USERSTYLE = 7 + PS_ALTERNATE = 8 + PS_STYLE_MASK = 0x0000000F +) + +// Pen cap types +const ( + PS_ENDCAP_ROUND = 0x00000000 + PS_ENDCAP_SQUARE = 0x00000100 + PS_ENDCAP_FLAT = 0x00000200 + PS_ENDCAP_MASK = 0x00000F00 +) + +// Pen join types +const ( + PS_JOIN_ROUND = 0x00000000 + PS_JOIN_BEVEL = 0x00001000 + PS_JOIN_MITER = 0x00002000 + PS_JOIN_MASK = 0x0000F000 +) + +// Hatch styles +const ( + HS_HORIZONTAL = 0 + HS_VERTICAL = 1 + HS_FDIAGONAL = 2 + HS_BDIAGONAL = 3 + HS_CROSS = 4 + HS_DIAGCROSS = 5 +) + +// Stock Logical Objects +const ( + WHITE_BRUSH = 0 + LTGRAY_BRUSH = 1 + GRAY_BRUSH = 2 + DKGRAY_BRUSH = 3 + BLACK_BRUSH = 4 + NULL_BRUSH = 5 + HOLLOW_BRUSH = NULL_BRUSH + WHITE_PEN = 6 + BLACK_PEN = 7 + NULL_PEN = 8 + OEM_FIXED_FONT = 10 + ANSI_FIXED_FONT = 11 + ANSI_VAR_FONT = 12 + SYSTEM_FONT = 13 + DEVICE_DEFAULT_FONT = 14 + DEFAULT_PALETTE = 15 + SYSTEM_FIXED_FONT = 16 + DEFAULT_GUI_FONT = 17 + DC_BRUSH = 18 + DC_PEN = 19 +) + +// Brush styles +const ( + BS_SOLID = 0 + BS_NULL = 1 + BS_HOLLOW = BS_NULL + BS_HATCHED = 2 + BS_PATTERN = 3 + BS_INDEXED = 4 + BS_DIBPATTERN = 5 + BS_DIBPATTERNPT = 6 + BS_PATTERN8X8 = 7 + BS_DIBPATTERN8X8 = 8 + BS_MONOPATTERN = 9 +) + +// TRACKMOUSEEVENT flags +const ( + TME_HOVER = 0x00000001 + TME_LEAVE = 0x00000002 + TME_NONCLIENT = 0x00000010 + TME_QUERY = 0x40000000 + TME_CANCEL = 0x80000000 + + HOVER_DEFAULT = 0xFFFFFFFF +) + +// WM_NCHITTEST and MOUSEHOOKSTRUCT Mouse Position Codes +const ( + HTERROR = (-2) + HTTRANSPARENT = (-1) + HTNOWHERE = 0 + HTCLIENT = 1 + HTCAPTION = 2 + HTSYSMENU = 3 + HTGROWBOX = 4 + HTSIZE = HTGROWBOX + HTMENU = 5 + HTHSCROLL = 6 + HTVSCROLL = 7 + HTMINBUTTON = 8 + HTMAXBUTTON = 9 + HTLEFT = 10 + HTRIGHT = 11 + HTTOP = 12 + HTTOPLEFT = 13 + HTTOPRIGHT = 14 + HTBOTTOM = 15 + HTBOTTOMLEFT = 16 + HTBOTTOMRIGHT = 17 + HTBORDER = 18 + HTREDUCE = HTMINBUTTON + HTZOOM = HTMAXBUTTON + HTSIZEFIRST = HTLEFT + HTSIZELAST = HTBOTTOMRIGHT + HTOBJECT = 19 + HTCLOSE = 20 + HTHELP = 21 +) + +// DrawText[Ex] format flags +const ( + DT_TOP = 0x00000000 + DT_LEFT = 0x00000000 + DT_CENTER = 0x00000001 + DT_RIGHT = 0x00000002 + DT_VCENTER = 0x00000004 + DT_BOTTOM = 0x00000008 + DT_WORDBREAK = 0x00000010 + DT_SINGLELINE = 0x00000020 + DT_EXPANDTABS = 0x00000040 + DT_TABSTOP = 0x00000080 + DT_NOCLIP = 0x00000100 + DT_EXTERNALLEADING = 0x00000200 + DT_CALCRECT = 0x00000400 + DT_NOPREFIX = 0x00000800 + DT_INTERNAL = 0x00001000 + DT_EDITCONTROL = 0x00002000 + DT_PATH_ELLIPSIS = 0x00004000 + DT_END_ELLIPSIS = 0x00008000 + DT_MODIFYSTRING = 0x00010000 + DT_RTLREADING = 0x00020000 + DT_WORD_ELLIPSIS = 0x00040000 + DT_NOFULLWIDTHCHARBREAK = 0x00080000 + DT_HIDEPREFIX = 0x00100000 + DT_PREFIXONLY = 0x00200000 +) + +const CLR_INVALID = 0xFFFFFFFF + +// Background Modes +const ( + TRANSPARENT = 1 + OPAQUE = 2 + BKMODE_LAST = 2 +) + +// Global Memory Flags +const ( + GMEM_FIXED = 0x0000 + GMEM_MOVEABLE = 0x0002 + GMEM_NOCOMPACT = 0x0010 + GMEM_NODISCARD = 0x0020 + GMEM_ZEROINIT = 0x0040 + GMEM_MODIFY = 0x0080 + GMEM_DISCARDABLE = 0x0100 + GMEM_NOT_BANKED = 0x1000 + GMEM_SHARE = 0x2000 + GMEM_DDESHARE = 0x2000 + GMEM_NOTIFY = 0x4000 + GMEM_LOWER = GMEM_NOT_BANKED + GMEM_VALID_FLAGS = 0x7F72 + GMEM_INVALID_HANDLE = 0x8000 + GHND = (GMEM_MOVEABLE | GMEM_ZEROINIT) + GPTR = (GMEM_FIXED | GMEM_ZEROINIT) +) + +// Ternary raster operations +const ( + SRCCOPY = 0x00CC0020 + SRCPAINT = 0x00EE0086 + SRCAND = 0x008800C6 + SRCINVERT = 0x00660046 + SRCERASE = 0x00440328 + NOTSRCCOPY = 0x00330008 + NOTSRCERASE = 0x001100A6 + MERGECOPY = 0x00C000CA + MERGEPAINT = 0x00BB0226 + PATCOPY = 0x00F00021 + PATPAINT = 0x00FB0A09 + PATINVERT = 0x005A0049 + DSTINVERT = 0x00550009 + BLACKNESS = 0x00000042 + WHITENESS = 0x00FF0062 + NOMIRRORBITMAP = 0x80000000 + CAPTUREBLT = 0x40000000 +) + +// Clipboard formats +const ( + CF_TEXT = 1 + CF_BITMAP = 2 + CF_METAFILEPICT = 3 + CF_SYLK = 4 + CF_DIF = 5 + CF_TIFF = 6 + CF_OEMTEXT = 7 + CF_DIB = 8 + CF_PALETTE = 9 + CF_PENDATA = 10 + CF_RIFF = 11 + CF_WAVE = 12 + CF_UNICODETEXT = 13 + CF_ENHMETAFILE = 14 + CF_HDROP = 15 + CF_LOCALE = 16 + CF_DIBV5 = 17 + CF_MAX = 18 + CF_OWNERDISPLAY = 0x0080 + CF_DSPTEXT = 0x0081 + CF_DSPBITMAP = 0x0082 + CF_DSPMETAFILEPICT = 0x0083 + CF_DSPENHMETAFILE = 0x008E + CF_PRIVATEFIRST = 0x0200 + CF_PRIVATELAST = 0x02FF + CF_GDIOBJFIRST = 0x0300 + CF_GDIOBJLAST = 0x03FF +) + +// Bitmap compression formats +const ( + BI_RGB = 0 + BI_RLE8 = 1 + BI_RLE4 = 2 + BI_BITFIELDS = 3 + BI_JPEG = 4 + BI_PNG = 5 +) + +// SetDIBitsToDevice fuColorUse +const ( + DIB_PAL_COLORS = 1 + DIB_RGB_COLORS = 0 +) + +const ( + STANDARD_RIGHTS_REQUIRED = 0x000F +) + +// Service Control Manager object specific access types +const ( + SC_MANAGER_CONNECT = 0x0001 + SC_MANAGER_CREATE_SERVICE = 0x0002 + SC_MANAGER_ENUMERATE_SERVICE = 0x0004 + SC_MANAGER_LOCK = 0x0008 + SC_MANAGER_QUERY_LOCK_STATUS = 0x0010 + SC_MANAGER_MODIFY_BOOT_CONFIG = 0x0020 + SC_MANAGER_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE | SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_LOCK | SC_MANAGER_QUERY_LOCK_STATUS | SC_MANAGER_MODIFY_BOOT_CONFIG +) + +// Service Types (Bit Mask) +const ( + SERVICE_KERNEL_DRIVER = 0x00000001 + SERVICE_FILE_SYSTEM_DRIVER = 0x00000002 + SERVICE_ADAPTER = 0x00000004 + SERVICE_RECOGNIZER_DRIVER = 0x00000008 + SERVICE_DRIVER = SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_RECOGNIZER_DRIVER + SERVICE_WIN32_OWN_PROCESS = 0x00000010 + SERVICE_WIN32_SHARE_PROCESS = 0x00000020 + SERVICE_WIN32 = SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS + SERVICE_INTERACTIVE_PROCESS = 0x00000100 + SERVICE_TYPE_ALL = SERVICE_WIN32 | SERVICE_ADAPTER | SERVICE_DRIVER | SERVICE_INTERACTIVE_PROCESS +) + +// Service State -- for CurrentState +const ( + SERVICE_STOPPED = 0x00000001 + SERVICE_START_PENDING = 0x00000002 + SERVICE_STOP_PENDING = 0x00000003 + SERVICE_RUNNING = 0x00000004 + SERVICE_CONTINUE_PENDING = 0x00000005 + SERVICE_PAUSE_PENDING = 0x00000006 + SERVICE_PAUSED = 0x00000007 +) + +// Controls Accepted (Bit Mask) +const ( + SERVICE_ACCEPT_STOP = 0x00000001 + SERVICE_ACCEPT_PAUSE_CONTINUE = 0x00000002 + SERVICE_ACCEPT_SHUTDOWN = 0x00000004 + SERVICE_ACCEPT_PARAMCHANGE = 0x00000008 + SERVICE_ACCEPT_NETBINDCHANGE = 0x00000010 + SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 0x00000020 + SERVICE_ACCEPT_POWEREVENT = 0x00000040 + SERVICE_ACCEPT_SESSIONCHANGE = 0x00000080 + SERVICE_ACCEPT_PRESHUTDOWN = 0x00000100 + SERVICE_ACCEPT_TIMECHANGE = 0x00000200 + SERVICE_ACCEPT_TRIGGEREVENT = 0x00000400 +) + +// Service object specific access type +const ( + SERVICE_QUERY_CONFIG = 0x0001 + SERVICE_CHANGE_CONFIG = 0x0002 + SERVICE_QUERY_STATUS = 0x0004 + SERVICE_ENUMERATE_DEPENDENTS = 0x0008 + SERVICE_START = 0x0010 + SERVICE_STOP = 0x0020 + SERVICE_PAUSE_CONTINUE = 0x0040 + SERVICE_INTERROGATE = 0x0080 + SERVICE_USER_DEFINED_CONTROL = 0x0100 + + SERVICE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | + SERVICE_QUERY_CONFIG | + SERVICE_CHANGE_CONFIG | + SERVICE_QUERY_STATUS | + SERVICE_ENUMERATE_DEPENDENTS | + SERVICE_START | + SERVICE_STOP | + SERVICE_PAUSE_CONTINUE | + SERVICE_INTERROGATE | + SERVICE_USER_DEFINED_CONTROL +) + +// MapVirtualKey maptypes +const ( + MAPVK_VK_TO_CHAR = 2 + MAPVK_VK_TO_VSC = 0 + MAPVK_VSC_TO_VK = 1 + MAPVK_VSC_TO_VK_EX = 3 +) + +// ReadEventLog Flags +const ( + EVENTLOG_SEEK_READ = 0x0002 + EVENTLOG_SEQUENTIAL_READ = 0x0001 + EVENTLOG_FORWARDS_READ = 0x0004 + EVENTLOG_BACKWARDS_READ = 0x0008 +) + +// CreateToolhelp32Snapshot flags +const ( + TH32CS_SNAPHEAPLIST = 0x00000001 + TH32CS_SNAPPROCESS = 0x00000002 + TH32CS_SNAPTHREAD = 0x00000004 + TH32CS_SNAPMODULE = 0x00000008 + TH32CS_SNAPMODULE32 = 0x00000010 + TH32CS_INHERIT = 0x80000000 + TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPMODULE | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD +) + +const ( + MAX_MODULE_NAME32 = 255 + MAX_PATH = 260 +) + +const ( + FOREGROUND_BLUE = 0x0001 + FOREGROUND_GREEN = 0x0002 + FOREGROUND_RED = 0x0004 + FOREGROUND_INTENSITY = 0x0008 + BACKGROUND_BLUE = 0x0010 + BACKGROUND_GREEN = 0x0020 + BACKGROUND_RED = 0x0040 + BACKGROUND_INTENSITY = 0x0080 + COMMON_LVB_LEADING_BYTE = 0x0100 + COMMON_LVB_TRAILING_BYTE = 0x0200 + COMMON_LVB_GRID_HORIZONTAL = 0x0400 + COMMON_LVB_GRID_LVERTICAL = 0x0800 + COMMON_LVB_GRID_RVERTICAL = 0x1000 + COMMON_LVB_REVERSE_VIDEO = 0x4000 + COMMON_LVB_UNDERSCORE = 0x8000 +) + +// Flags used by the DWM_BLURBEHIND structure to indicate +// which of its members contain valid information. +const ( + DWM_BB_ENABLE = 0x00000001 // A value for the fEnable member has been specified. + DWM_BB_BLURREGION = 0x00000002 // A value for the hRgnBlur member has been specified. + DWM_BB_TRANSITIONONMAXIMIZED = 0x00000004 // A value for the fTransitionOnMaximized member has been specified. +) + +// Flags used by the DwmEnableComposition function +// to change the state of Desktop Window Manager (DWM) composition. +const ( + DWM_EC_DISABLECOMPOSITION = 0 // Disable composition + DWM_EC_ENABLECOMPOSITION = 1 // Enable composition +) + +// enum-lite implementation for the following constant structure +type DWM_SHOWCONTACT int32 + +const ( + DWMSC_DOWN = 0x00000001 + DWMSC_UP = 0x00000002 + DWMSC_DRAG = 0x00000004 + DWMSC_HOLD = 0x00000008 + DWMSC_PENBARREL = 0x00000010 + DWMSC_NONE = 0x00000000 + DWMSC_ALL = 0xFFFFFFFF +) + +// enum-lite implementation for the following constant structure +type DWM_SOURCE_FRAME_SAMPLING int32 + +// TODO: need to verify this construction +// Flags used by the DwmSetPresentParameters function +// to specify the frame sampling type +const ( + DWM_SOURCE_FRAME_SAMPLING_POINT = iota + 1 + DWM_SOURCE_FRAME_SAMPLING_COVERAGE + DWM_SOURCE_FRAME_SAMPLING_LAST +) + +// Flags used by the DWM_THUMBNAIL_PROPERTIES structure to +// indicate which of its members contain valid information. +const ( + DWM_TNP_RECTDESTINATION = 0x00000001 // A value for the rcDestination member has been specified + DWM_TNP_RECTSOURCE = 0x00000002 // A value for the rcSource member has been specified + DWM_TNP_OPACITY = 0x00000004 // A value for the opacity member has been specified + DWM_TNP_VISIBLE = 0x00000008 // A value for the fVisible member has been specified + DWM_TNP_SOURCECLIENTAREAONLY = 0x00000010 // A value for the fSourceClientAreaOnly member has been specified +) + +// enum-lite implementation for the following constant structure +type DWMFLIP3DWINDOWPOLICY int32 + +// TODO: need to verify this construction +// Flags used by the DwmSetWindowAttribute function +// to specify the Flip3D window policy +const ( + DWMFLIP3D_DEFAULT = iota + 1 + DWMFLIP3D_EXCLUDEBELOW + DWMFLIP3D_EXCLUDEABOVE + DWMFLIP3D_LAST +) + +// enum-lite implementation for the following constant structure +type DWMNCRENDERINGPOLICY int32 + +// TODO: need to verify this construction +// Flags used by the DwmSetWindowAttribute function +// to specify the non-client area rendering policy +const ( + DWMNCRP_USEWINDOWSTYLE = iota + 1 + DWMNCRP_DISABLED + DWMNCRP_ENABLED + DWMNCRP_LAST +) + +// enum-lite implementation for the following constant structure +type DWMTRANSITION_OWNEDWINDOW_TARGET int32 + +const ( + DWMTRANSITION_OWNEDWINDOW_NULL = -1 + DWMTRANSITION_OWNEDWINDOW_REPOSITION = 0 +) + +// enum-lite implementation for the following constant structure +type DWMWINDOWATTRIBUTE int32 + +// TODO: need to verify this construction +// Flags used by the DwmGetWindowAttribute and DwmSetWindowAttribute functions +// to specify window attributes for non-client rendering +const ( + DWMWA_NCRENDERING_ENABLED = iota + 1 + DWMWA_NCRENDERING_POLICY + DWMWA_TRANSITIONS_FORCEDISABLED + DWMWA_ALLOW_NCPAINT + DWMWA_CAPTION_BUTTON_BOUNDS + DWMWA_NONCLIENT_RTL_LAYOUT + DWMWA_FORCE_ICONIC_REPRESENTATION + DWMWA_FLIP3D_POLICY + DWMWA_EXTENDED_FRAME_BOUNDS + DWMWA_HAS_ICONIC_BITMAP + DWMWA_DISALLOW_PEEK + DWMWA_EXCLUDED_FROM_PEEK + DWMWA_CLOAK + DWMWA_CLOAKED + DWMWA_FREEZE_REPRESENTATION + DWMWA_LAST +) + +// enum-lite implementation for the following constant structure +type GESTURE_TYPE int32 + +// TODO: use iota? +// Identifies the gesture type +const ( + GT_PEN_TAP = 0 + GT_PEN_DOUBLETAP = 1 + GT_PEN_RIGHTTAP = 2 + GT_PEN_PRESSANDHOLD = 3 + GT_PEN_PRESSANDHOLDABORT = 4 + GT_TOUCH_TAP = 5 + GT_TOUCH_DOUBLETAP = 6 + GT_TOUCH_RIGHTTAP = 7 + GT_TOUCH_PRESSANDHOLD = 8 + GT_TOUCH_PRESSANDHOLDABORT = 9 + GT_TOUCH_PRESSANDTAP = 10 +) + +// Icons +const ( + ICON_SMALL = 0 + ICON_BIG = 1 + ICON_SMALL2 = 2 +) + +const ( + SIZE_RESTORED = 0 + SIZE_MINIMIZED = 1 + SIZE_MAXIMIZED = 2 + SIZE_MAXSHOW = 3 + SIZE_MAXHIDE = 4 +) + +// XButton values +const ( + XBUTTON1 = 1 + XBUTTON2 = 2 +) + +// Devmode +const ( + DM_SPECVERSION = 0x0401 + + DM_ORIENTATION = 0x00000001 + DM_PAPERSIZE = 0x00000002 + DM_PAPERLENGTH = 0x00000004 + DM_PAPERWIDTH = 0x00000008 + DM_SCALE = 0x00000010 + DM_POSITION = 0x00000020 + DM_NUP = 0x00000040 + DM_DISPLAYORIENTATION = 0x00000080 + DM_COPIES = 0x00000100 + DM_DEFAULTSOURCE = 0x00000200 + DM_PRINTQUALITY = 0x00000400 + DM_COLOR = 0x00000800 + DM_DUPLEX = 0x00001000 + DM_YRESOLUTION = 0x00002000 + DM_TTOPTION = 0x00004000 + DM_COLLATE = 0x00008000 + DM_FORMNAME = 0x00010000 + DM_LOGPIXELS = 0x00020000 + DM_BITSPERPEL = 0x00040000 + DM_PELSWIDTH = 0x00080000 + DM_PELSHEIGHT = 0x00100000 + DM_DISPLAYFLAGS = 0x00200000 + DM_DISPLAYFREQUENCY = 0x00400000 + DM_ICMMETHOD = 0x00800000 + DM_ICMINTENT = 0x01000000 + DM_MEDIATYPE = 0x02000000 + DM_DITHERTYPE = 0x04000000 + DM_PANNINGWIDTH = 0x08000000 + DM_PANNINGHEIGHT = 0x10000000 + DM_DISPLAYFIXEDOUTPUT = 0x20000000 +) + +// ChangeDisplaySettings +const ( + CDS_UPDATEREGISTRY = 0x00000001 + CDS_TEST = 0x00000002 + CDS_FULLSCREEN = 0x00000004 + CDS_GLOBAL = 0x00000008 + CDS_SET_PRIMARY = 0x00000010 + CDS_VIDEOPARAMETERS = 0x00000020 + CDS_RESET = 0x40000000 + CDS_NORESET = 0x10000000 + + DISP_CHANGE_SUCCESSFUL = 0 + DISP_CHANGE_RESTART = 1 + DISP_CHANGE_FAILED = -1 + DISP_CHANGE_BADMODE = -2 + DISP_CHANGE_NOTUPDATED = -3 + DISP_CHANGE_BADFLAGS = -4 + DISP_CHANGE_BADPARAM = -5 + DISP_CHANGE_BADDUALVIEW = -6 +) + +const ( + ENUM_CURRENT_SETTINGS = 0xFFFFFFFF + ENUM_REGISTRY_SETTINGS = 0xFFFFFFFE +) + +// PIXELFORMATDESCRIPTOR +const ( + PFD_TYPE_RGBA = 0 + PFD_TYPE_COLORINDEX = 1 + + PFD_MAIN_PLANE = 0 + PFD_OVERLAY_PLANE = 1 + PFD_UNDERLAY_PLANE = -1 + + PFD_DOUBLEBUFFER = 0x00000001 + PFD_STEREO = 0x00000002 + PFD_DRAW_TO_WINDOW = 0x00000004 + PFD_DRAW_TO_BITMAP = 0x00000008 + PFD_SUPPORT_GDI = 0x00000010 + PFD_SUPPORT_OPENGL = 0x00000020 + PFD_GENERIC_FORMAT = 0x00000040 + PFD_NEED_PALETTE = 0x00000080 + PFD_NEED_SYSTEM_PALETTE = 0x00000100 + PFD_SWAP_EXCHANGE = 0x00000200 + PFD_SWAP_COPY = 0x00000400 + PFD_SWAP_LAYER_BUFFERS = 0x00000800 + PFD_GENERIC_ACCELERATED = 0x00001000 + PFD_SUPPORT_DIRECTDRAW = 0x00002000 + PFD_DIRECT3D_ACCELERATED = 0x00004000 + PFD_SUPPORT_COMPOSITION = 0x00008000 + + PFD_DEPTH_DONTCARE = 0x20000000 + PFD_DOUBLEBUFFER_DONTCARE = 0x40000000 + PFD_STEREO_DONTCARE = 0x80000000 +) + +const ( + INPUT_MOUSE = 0 + INPUT_KEYBOARD = 1 + INPUT_HARDWARE = 2 +) + +const ( + MOUSEEVENTF_ABSOLUTE = 0x8000 + MOUSEEVENTF_HWHEEL = 0x01000 + MOUSEEVENTF_MOVE = 0x0001 + MOUSEEVENTF_MOVE_NOCOALESCE = 0x2000 + MOUSEEVENTF_LEFTDOWN = 0x0002 + MOUSEEVENTF_LEFTUP = 0x0004 + MOUSEEVENTF_RIGHTDOWN = 0x0008 + MOUSEEVENTF_RIGHTUP = 0x0010 + MOUSEEVENTF_MIDDLEDOWN = 0x0020 + MOUSEEVENTF_MIDDLEUP = 0x0040 + MOUSEEVENTF_VIRTUALDESK = 0x4000 + MOUSEEVENTF_WHEEL = 0x0800 + MOUSEEVENTF_XDOWN = 0x0080 + MOUSEEVENTF_XUP = 0x0100 +) + +// Windows Hooks (WH_*) +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx +const ( + WH_CALLWNDPROC = 4 + WH_CALLWNDPROCRET = 12 + WH_CBT = 5 + WH_DEBUG = 9 + WH_FOREGROUNDIDLE = 11 + WH_GETMESSAGE = 3 + WH_JOURNALPLAYBACK = 1 + WH_JOURNALRECORD = 0 + WH_KEYBOARD = 2 + WH_KEYBOARD_LL = 13 + WH_MOUSE = 7 + WH_MOUSE_LL = 14 + WH_MSGFILTER = -1 + WH_SHELL = 10 + WH_SYSMSGFILTER = 6 +) + +// ComboBox return values +const ( + CB_OKAY = 0 + CB_ERR = ^uintptr(0) // -1 + CB_ERRSPACE = ^uintptr(1) // -2 +) + +// ComboBox notifications +const ( + CBN_ERRSPACE = -1 + CBN_SELCHANGE = 1 + CBN_DBLCLK = 2 + CBN_SETFOCUS = 3 + CBN_KILLFOCUS = 4 + CBN_EDITCHANGE = 5 + CBN_EDITUPDATE = 6 + CBN_DROPDOWN = 7 + CBN_CLOSEUP = 8 + CBN_SELENDOK = 9 + CBN_SELENDCANCEL = 10 +) + +// ComboBox styles +const ( + CBS_SIMPLE = 0x0001 + CBS_DROPDOWN = 0x0002 + CBS_DROPDOWNLIST = 0x0003 + CBS_OWNERDRAWFIXED = 0x0010 + CBS_OWNERDRAWVARIABLE = 0x0020 + CBS_AUTOHSCROLL = 0x0040 + CBS_OEMCONVERT = 0x0080 + CBS_SORT = 0x0100 + CBS_HASSTRINGS = 0x0200 + CBS_NOINTEGRALHEIGHT = 0x0400 + CBS_DISABLENOSCROLL = 0x0800 + CBS_UPPERCASE = 0x2000 + CBS_LOWERCASE = 0x4000 +) + +// ComboBox messages +const ( + CB_GETEDITSEL = 0x0140 + CB_LIMITTEXT = 0x0141 + CB_SETEDITSEL = 0x0142 + CB_ADDSTRING = 0x0143 + CB_DELETESTRING = 0x0144 + CB_DIR = 0x0145 + CB_GETCOUNT = 0x0146 + CB_GETCURSEL = 0x0147 + CB_GETLBTEXT = 0x0148 + CB_GETLBTEXTLEN = 0x0149 + CB_INSERTSTRING = 0x014A + CB_RESETCONTENT = 0x014B + CB_FINDSTRING = 0x014C + CB_SELECTSTRING = 0x014D + CB_SETCURSEL = 0x014E + CB_SHOWDROPDOWN = 0x014F + CB_GETITEMDATA = 0x0150 + CB_SETITEMDATA = 0x0151 + CB_GETDROPPEDCONTROLRECT = 0x0152 + CB_SETITEMHEIGHT = 0x0153 + CB_GETITEMHEIGHT = 0x0154 + CB_SETEXTENDEDUI = 0x0155 + CB_GETEXTENDEDUI = 0x0156 + CB_GETDROPPEDSTATE = 0x0157 + CB_FINDSTRINGEXACT = 0x0158 + CB_SETLOCALE = 0x0159 + CB_GETLOCALE = 0x015A + CB_GETTOPINDEX = 0x015b + CB_SETTOPINDEX = 0x015c + CB_GETHORIZONTALEXTENT = 0x015d + CB_SETHORIZONTALEXTENT = 0x015e + CB_GETDROPPEDWIDTH = 0x015f + CB_SETDROPPEDWIDTH = 0x0160 + CB_INITSTORAGE = 0x0161 + CB_MULTIPLEADDSTRING = 0x0163 + CB_GETCOMBOBOXINFO = 0x0164 +) + +// TreeView styles +const ( + TVS_HASBUTTONS = 0x0001 + TVS_HASLINES = 0x0002 + TVS_LINESATROOT = 0x0004 + TVS_EDITLABELS = 0x0008 + TVS_DISABLEDRAGDROP = 0x0010 + TVS_SHOWSELALWAYS = 0x0020 + TVS_RTLREADING = 0x0040 + TVS_NOTOOLTIPS = 0x0080 + TVS_CHECKBOXES = 0x0100 + TVS_TRACKSELECT = 0x0200 + TVS_SINGLEEXPAND = 0x0400 + TVS_INFOTIP = 0x0800 + TVS_FULLROWSELECT = 0x1000 + TVS_NOSCROLL = 0x2000 + TVS_NONEVENHEIGHT = 0x4000 + TVS_NOHSCROLL = 0x8000 +) + +const ( + TVS_EX_NOSINGLECOLLAPSE = 0x0001 + TVS_EX_MULTISELECT = 0x0002 + TVS_EX_DOUBLEBUFFER = 0x0004 + TVS_EX_NOINDENTSTATE = 0x0008 + TVS_EX_RICHTOOLTIP = 0x0010 + TVS_EX_AUTOHSCROLL = 0x0020 + TVS_EX_FADEINOUTEXPANDOS = 0x0040 + TVS_EX_PARTIALCHECKBOXES = 0x0080 + TVS_EX_EXCLUSIONCHECKBOXES = 0x0100 + TVS_EX_DIMMEDCHECKBOXES = 0x0200 + TVS_EX_DRAWIMAGEASYNC = 0x0400 +) + +const ( + TVIF_TEXT = 0x0001 + TVIF_IMAGE = 0x0002 + TVIF_PARAM = 0x0004 + TVIF_STATE = 0x0008 + TVIF_HANDLE = 0x0010 + TVIF_SELECTEDIMAGE = 0x0020 + TVIF_CHILDREN = 0x0040 + TVIF_INTEGRAL = 0x0080 + TVIF_STATEEX = 0x0100 + TVIF_EXPANDEDIMAGE = 0x0200 +) + +const ( + TVIS_SELECTED = 0x0002 + TVIS_CUT = 0x0004 + TVIS_DROPHILITED = 0x0008 + TVIS_BOLD = 0x0010 + TVIS_EXPANDED = 0x0020 + TVIS_EXPANDEDONCE = 0x0040 + TVIS_EXPANDPARTIAL = 0x0080 + TVIS_OVERLAYMASK = 0x0F00 + TVIS_STATEIMAGEMASK = 0xF000 + TVIS_USERMASK = 0xF000 +) + +const ( + TVIS_EX_FLAT = 0x0001 + TVIS_EX_DISABLED = 0x0002 + TVIS_EX_ALL = 0x0002 +) + +const ( + TVI_ROOT = ^HTREEITEM(0xffff) + TVI_FIRST = ^HTREEITEM(0xfffe) + TVI_LAST = ^HTREEITEM(0xfffd) + TVI_SORT = ^HTREEITEM(0xfffc) +) + +// TVM_EXPAND action flags +const ( + TVE_COLLAPSE = 0x0001 + TVE_EXPAND = 0x0002 + TVE_TOGGLE = 0x0003 + TVE_EXPANDPARTIAL = 0x4000 + TVE_COLLAPSERESET = 0x8000 +) + +const ( + TVGN_CARET = 9 +) + +// TreeView messages +const ( + TV_FIRST = 0x1100 + + TVM_INSERTITEM = TV_FIRST + 50 + TVM_DELETEITEM = TV_FIRST + 1 + TVM_EXPAND = TV_FIRST + 2 + TVM_GETITEMRECT = TV_FIRST + 4 + TVM_GETCOUNT = TV_FIRST + 5 + TVM_GETINDENT = TV_FIRST + 6 + TVM_SETINDENT = TV_FIRST + 7 + TVM_GETIMAGELIST = TV_FIRST + 8 + TVM_SETIMAGELIST = TV_FIRST + 9 + TVM_GETNEXTITEM = TV_FIRST + 10 + TVM_SELECTITEM = TV_FIRST + 11 + TVM_GETITEM = TV_FIRST + 62 + TVM_SETITEM = TV_FIRST + 63 + TVM_EDITLABEL = TV_FIRST + 65 + TVM_GETEDITCONTROL = TV_FIRST + 15 + TVM_GETVISIBLECOUNT = TV_FIRST + 16 + TVM_HITTEST = TV_FIRST + 17 + TVM_CREATEDRAGIMAGE = TV_FIRST + 18 + TVM_SORTCHILDREN = TV_FIRST + 19 + TVM_ENSUREVISIBLE = TV_FIRST + 20 + TVM_SORTCHILDRENCB = TV_FIRST + 21 + TVM_ENDEDITLABELNOW = TV_FIRST + 22 + TVM_GETISEARCHSTRING = TV_FIRST + 64 + TVM_SETTOOLTIPS = TV_FIRST + 24 + TVM_GETTOOLTIPS = TV_FIRST + 25 + TVM_SETINSERTMARK = TV_FIRST + 26 + TVM_SETUNICODEFORMAT = CCM_SETUNICODEFORMAT + TVM_GETUNICODEFORMAT = CCM_GETUNICODEFORMAT + TVM_SETITEMHEIGHT = TV_FIRST + 27 + TVM_GETITEMHEIGHT = TV_FIRST + 28 + TVM_SETBKCOLOR = TV_FIRST + 29 + TVM_SETTEXTCOLOR = TV_FIRST + 30 + TVM_GETBKCOLOR = TV_FIRST + 31 + TVM_GETTEXTCOLOR = TV_FIRST + 32 + TVM_SETSCROLLTIME = TV_FIRST + 33 + TVM_GETSCROLLTIME = TV_FIRST + 34 + TVM_SETINSERTMARKCOLOR = TV_FIRST + 37 + TVM_GETINSERTMARKCOLOR = TV_FIRST + 38 + TVM_GETITEMSTATE = TV_FIRST + 39 + TVM_SETLINECOLOR = TV_FIRST + 40 + TVM_GETLINECOLOR = TV_FIRST + 41 + TVM_MAPACCIDTOHTREEITEM = TV_FIRST + 42 + TVM_MAPHTREEITEMTOACCID = TV_FIRST + 43 + TVM_SETEXTENDEDSTYLE = TV_FIRST + 44 + TVM_GETEXTENDEDSTYLE = TV_FIRST + 45 + TVM_SETAUTOSCROLLINFO = TV_FIRST + 59 +) + +// TreeView notifications +const ( + TVN_FIRST = ^uint32(399) + + TVN_SELCHANGING = TVN_FIRST - 50 + TVN_SELCHANGED = TVN_FIRST - 51 + TVN_GETDISPINFO = TVN_FIRST - 52 + TVN_ITEMEXPANDING = TVN_FIRST - 54 + TVN_ITEMEXPANDED = TVN_FIRST - 55 + TVN_BEGINDRAG = TVN_FIRST - 56 + TVN_BEGINRDRAG = TVN_FIRST - 57 + TVN_DELETEITEM = TVN_FIRST - 58 + TVN_BEGINLABELEDIT = TVN_FIRST - 59 + TVN_ENDLABELEDIT = TVN_FIRST - 60 + TVN_KEYDOWN = TVN_FIRST - 12 + TVN_GETINFOTIP = TVN_FIRST - 14 + TVN_SINGLEEXPAND = TVN_FIRST - 15 + TVN_ITEMCHANGING = TVN_FIRST - 17 + TVN_ITEMCHANGED = TVN_FIRST - 19 + TVN_ASYNCDRAW = TVN_FIRST - 20 +) + +// TreeView hit test constants +const ( + TVHT_NOWHERE = 1 + TVHT_ONITEMICON = 2 + TVHT_ONITEMLABEL = 4 + TVHT_ONITEM = TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMSTATEICON + TVHT_ONITEMINDENT = 8 + TVHT_ONITEMBUTTON = 16 + TVHT_ONITEMRIGHT = 32 + TVHT_ONITEMSTATEICON = 64 + TVHT_ABOVE = 256 + TVHT_BELOW = 512 + TVHT_TORIGHT = 1024 + TVHT_TOLEFT = 2048 +) + +type HTREEITEM HANDLE + +type TVITEM struct { + Mask uint32 + HItem HTREEITEM + State uint32 + StateMask uint32 + PszText uintptr + CchTextMax int32 + IImage int32 + ISelectedImage int32 + CChildren int32 + LParam uintptr +} + +/*type TVITEMEX struct { + mask UINT + hItem HTREEITEM + state UINT + stateMask UINT + pszText LPWSTR + cchTextMax int + iImage int + iSelectedImage int + cChildren int + lParam LPARAM + iIntegral int + uStateEx UINT + hwnd HWND + iExpandedImage int +}*/ + +type TVINSERTSTRUCT struct { + HParent HTREEITEM + HInsertAfter HTREEITEM + Item TVITEM + // itemex TVITEMEX +} + +type NMTREEVIEW struct { + Hdr NMHDR + Action uint32 + ItemOld TVITEM + ItemNew TVITEM + PtDrag POINT +} + +type NMTVDISPINFO struct { + Hdr NMHDR + Item TVITEM +} + +type NMTVKEYDOWN struct { + Hdr NMHDR + WVKey uint16 + Flags uint32 +} + +type TVHITTESTINFO struct { + Pt POINT + Flags uint32 + HItem HTREEITEM +} + +// TabPage support + +const TCM_FIRST = 0x1300 +const TCN_FIRST = -550 + +const ( + TCS_SCROLLOPPOSITE = 0x0001 + TCS_BOTTOM = 0x0002 + TCS_RIGHT = 0x0002 + TCS_MULTISELECT = 0x0004 + TCS_FLATBUTTONS = 0x0008 + TCS_FORCEICONLEFT = 0x0010 + TCS_FORCELABELLEFT = 0x0020 + TCS_HOTTRACK = 0x0040 + TCS_VERTICAL = 0x0080 + TCS_TABS = 0x0000 + TCS_BUTTONS = 0x0100 + TCS_SINGLELINE = 0x0000 + TCS_MULTILINE = 0x0200 + TCS_RIGHTJUSTIFY = 0x0000 + TCS_FIXEDWIDTH = 0x0400 + TCS_RAGGEDRIGHT = 0x0800 + TCS_FOCUSONBUTTONDOWN = 0x1000 + TCS_OWNERDRAWFIXED = 0x2000 + TCS_TOOLTIPS = 0x4000 + TCS_FOCUSNEVER = 0x8000 +) + +const ( + TCS_EX_FLATSEPARATORS = 0x00000001 + TCS_EX_REGISTERDROP = 0x00000002 +) + +const ( + TCM_GETIMAGELIST = TCM_FIRST + 2 + TCM_SETIMAGELIST = TCM_FIRST + 3 + TCM_GETITEMCOUNT = TCM_FIRST + 4 + TCM_GETITEM = TCM_FIRST + 60 + TCM_SETITEM = TCM_FIRST + 61 + TCM_INSERTITEM = TCM_FIRST + 62 + TCM_DELETEITEM = TCM_FIRST + 8 + TCM_DELETEALLITEMS = TCM_FIRST + 9 + TCM_GETITEMRECT = TCM_FIRST + 10 + TCM_GETCURSEL = TCM_FIRST + 11 + TCM_SETCURSEL = TCM_FIRST + 12 + TCM_HITTEST = TCM_FIRST + 13 + TCM_SETITEMEXTRA = TCM_FIRST + 14 + TCM_ADJUSTRECT = TCM_FIRST + 40 + TCM_SETITEMSIZE = TCM_FIRST + 41 + TCM_REMOVEIMAGE = TCM_FIRST + 42 + TCM_SETPADDING = TCM_FIRST + 43 + TCM_GETROWCOUNT = TCM_FIRST + 44 + TCM_GETTOOLTIPS = TCM_FIRST + 45 + TCM_SETTOOLTIPS = TCM_FIRST + 46 + TCM_GETCURFOCUS = TCM_FIRST + 47 + TCM_SETCURFOCUS = TCM_FIRST + 48 + TCM_SETMINTABWIDTH = TCM_FIRST + 49 + TCM_DESELECTALL = TCM_FIRST + 50 + TCM_HIGHLIGHTITEM = TCM_FIRST + 51 + TCM_SETEXTENDEDSTYLE = TCM_FIRST + 52 + TCM_GETEXTENDEDSTYLE = TCM_FIRST + 53 + TCM_SETUNICODEFORMAT = CCM_SETUNICODEFORMAT + TCM_GETUNICODEFORMAT = CCM_GETUNICODEFORMAT +) + +const ( + TCIF_TEXT = 0x0001 + TCIF_IMAGE = 0x0002 + TCIF_RTLREADING = 0x0004 + TCIF_PARAM = 0x0008 + TCIF_STATE = 0x0010 +) + +const ( + TCIS_BUTTONPRESSED = 0x0001 + TCIS_HIGHLIGHTED = 0x0002 +) + +const ( + TCHT_NOWHERE = 0x0001 + TCHT_ONITEMICON = 0x0002 + TCHT_ONITEMLABEL = 0x0004 + TCHT_ONITEM = TCHT_ONITEMICON | TCHT_ONITEMLABEL +) + +const ( + TCN_KEYDOWN = TCN_FIRST - 0 + TCN_SELCHANGE = TCN_FIRST - 1 + TCN_SELCHANGING = TCN_FIRST - 2 + TCN_GETOBJECT = TCN_FIRST - 3 + TCN_FOCUSCHANGE = TCN_FIRST - 4 +) + +type TCITEMHEADER struct { + Mask uint32 + LpReserved1 uint32 + LpReserved2 uint32 + PszText *uint16 + CchTextMax int32 + IImage int32 +} + +type TCITEM struct { + Mask uint32 + DwState uint32 + DwStateMask uint32 + PszText *uint16 + CchTextMax int32 + IImage int32 + LParam uintptr +} + +type TCHITTESTINFO struct { + Pt POINT + flags uint32 +} + +type NMTCKEYDOWN struct { + Hdr NMHDR + WVKey uint16 + Flags uint32 +} + +// Menu support constants + +// Constants for MENUITEMINFO.fMask +const ( + MIIM_STATE = 1 + MIIM_ID = 2 + MIIM_SUBMENU = 4 + MIIM_CHECKMARKS = 8 + MIIM_TYPE = 16 + MIIM_DATA = 32 + MIIM_STRING = 64 + MIIM_BITMAP = 128 + MIIM_FTYPE = 256 +) + +// Constants for MENUITEMINFO.fType +const ( + MFT_BITMAP = 4 + MFT_MENUBARBREAK = 32 + MFT_MENUBREAK = 64 + MFT_OWNERDRAW = 256 + MFT_RADIOCHECK = 512 + MFT_RIGHTJUSTIFY = 0x4000 + MFT_SEPARATOR = 0x800 + MFT_RIGHTORDER = 0x2000 + MFT_STRING = 0 +) + +// Constants for MENUITEMINFO.fState +const ( + MFS_CHECKED = 8 + MFS_DEFAULT = 4096 + MFS_DISABLED = 3 + MFS_ENABLED = 0 + MFS_GRAYED = 3 + MFS_HILITE = 128 + MFS_UNCHECKED = 0 + MFS_UNHILITE = 0 +) + +// Constants for MENUITEMINFO.hbmp* +const ( + HBMMENU_CALLBACK = -1 + HBMMENU_SYSTEM = 1 + HBMMENU_MBAR_RESTORE = 2 + HBMMENU_MBAR_MINIMIZE = 3 + HBMMENU_MBAR_CLOSE = 5 + HBMMENU_MBAR_CLOSE_D = 6 + HBMMENU_MBAR_MINIMIZE_D = 7 + HBMMENU_POPUP_CLOSE = 8 + HBMMENU_POPUP_RESTORE = 9 + HBMMENU_POPUP_MAXIMIZE = 10 + HBMMENU_POPUP_MINIMIZE = 11 +) + +// MENUINFO mask constants +const ( + MIM_APPLYTOSUBMENUS = 0x80000000 + MIM_BACKGROUND = 0x00000002 + MIM_HELPID = 0x00000004 + MIM_MAXHEIGHT = 0x00000001 + MIM_MENUDATA = 0x00000008 + MIM_STYLE = 0x00000010 +) + +// MENUINFO style constants +const ( + MNS_AUTODISMISS = 0x10000000 + MNS_CHECKORBMP = 0x04000000 + MNS_DRAGDROP = 0x20000000 + MNS_MODELESS = 0x40000000 + MNS_NOCHECK = 0x80000000 + MNS_NOTIFYBYPOS = 0x08000000 +) + +const ( + MF_BYCOMMAND = 0x00000000 + MF_BYPOSITION = 0x00000400 +) + +type MENUITEMINFO struct { + CbSize uint32 + FMask uint32 + FType uint32 + FState uint32 + WID uint32 + HSubMenu HMENU + HbmpChecked HBITMAP + HbmpUnchecked HBITMAP + DwItemData uintptr + DwTypeData *uint16 + Cch uint32 + HbmpItem HBITMAP +} + +type MENUINFO struct { + CbSize uint32 + FMask uint32 + DwStyle uint32 + CyMax uint32 + HbrBack HBRUSH + DwContextHelpID uint32 + DwMenuData uintptr +} + +// UI state constants +const ( + UIS_SET = 1 + UIS_CLEAR = 2 + UIS_INITIALIZE = 3 +) + +// UI state constants +const ( + UISF_HIDEFOCUS = 0x1 + UISF_HIDEACCEL = 0x2 + UISF_ACTIVE = 0x4 +) + +// Virtual key codes +const ( + VK_LBUTTON = 1 + VK_RBUTTON = 2 + VK_CANCEL = 3 + VK_MBUTTON = 4 + VK_XBUTTON1 = 5 + VK_XBUTTON2 = 6 + VK_BACK = 8 + VK_TAB = 9 + VK_CLEAR = 12 + VK_RETURN = 13 + VK_SHIFT = 16 + VK_CONTROL = 17 + VK_MENU = 18 + VK_PAUSE = 19 + VK_CAPITAL = 20 + VK_KANA = 0x15 + VK_HANGEUL = 0x15 + VK_HANGUL = 0x15 + VK_JUNJA = 0x17 + VK_FINAL = 0x18 + VK_HANJA = 0x19 + VK_KANJI = 0x19 + VK_ESCAPE = 0x1B + VK_CONVERT = 0x1C + VK_NONCONVERT = 0x1D + VK_ACCEPT = 0x1E + VK_MODECHANGE = 0x1F + VK_SPACE = 32 + VK_PRIOR = 33 + VK_NEXT = 34 + VK_END = 35 + VK_HOME = 36 + VK_LEFT = 37 + VK_UP = 38 + VK_RIGHT = 39 + VK_DOWN = 40 + VK_SELECT = 41 + VK_PRINT = 42 + VK_EXECUTE = 43 + VK_SNAPSHOT = 44 + VK_INSERT = 45 + VK_DELETE = 46 + VK_HELP = 47 + VK_LWIN = 0x5B + VK_RWIN = 0x5C + VK_APPS = 0x5D + VK_SLEEP = 0x5F + VK_NUMPAD0 = 0x60 + VK_NUMPAD1 = 0x61 + VK_NUMPAD2 = 0x62 + VK_NUMPAD3 = 0x63 + VK_NUMPAD4 = 0x64 + VK_NUMPAD5 = 0x65 + VK_NUMPAD6 = 0x66 + VK_NUMPAD7 = 0x67 + VK_NUMPAD8 = 0x68 + VK_NUMPAD9 = 0x69 + VK_MULTIPLY = 0x6A + VK_ADD = 0x6B + VK_SEPARATOR = 0x6C + VK_SUBTRACT = 0x6D + VK_DECIMAL = 0x6E + VK_DIVIDE = 0x6F + VK_F1 = 0x70 + VK_F2 = 0x71 + VK_F3 = 0x72 + VK_F4 = 0x73 + VK_F5 = 0x74 + VK_F6 = 0x75 + VK_F7 = 0x76 + VK_F8 = 0x77 + VK_F9 = 0x78 + VK_F10 = 0x79 + VK_F11 = 0x7A + VK_F12 = 0x7B + VK_F13 = 0x7C + VK_F14 = 0x7D + VK_F15 = 0x7E + VK_F16 = 0x7F + VK_F17 = 0x80 + VK_F18 = 0x81 + VK_F19 = 0x82 + VK_F20 = 0x83 + VK_F21 = 0x84 + VK_F22 = 0x85 + VK_F23 = 0x86 + VK_F24 = 0x87 + VK_NUMLOCK = 0x90 + VK_SCROLL = 0x91 + VK_LSHIFT = 0xA0 + VK_RSHIFT = 0xA1 + VK_LCONTROL = 0xA2 + VK_RCONTROL = 0xA3 + VK_LMENU = 0xA4 + VK_RMENU = 0xA5 + VK_BROWSER_BACK = 0xA6 + VK_BROWSER_FORWARD = 0xA7 + VK_BROWSER_REFRESH = 0xA8 + VK_BROWSER_STOP = 0xA9 + VK_BROWSER_SEARCH = 0xAA + VK_BROWSER_FAVORITES = 0xAB + VK_BROWSER_HOME = 0xAC + VK_VOLUME_MUTE = 0xAD + VK_VOLUME_DOWN = 0xAE + VK_VOLUME_UP = 0xAF + VK_MEDIA_NEXT_TRACK = 0xB0 + VK_MEDIA_PREV_TRACK = 0xB1 + VK_MEDIA_STOP = 0xB2 + VK_MEDIA_PLAY_PAUSE = 0xB3 + VK_LAUNCH_MAIL = 0xB4 + VK_LAUNCH_MEDIA_SELECT = 0xB5 + VK_LAUNCH_APP1 = 0xB6 + VK_LAUNCH_APP2 = 0xB7 + VK_OEM_1 = 0xBA + VK_OEM_PLUS = 0xBB + VK_OEM_COMMA = 0xBC + VK_OEM_MINUS = 0xBD + VK_OEM_PERIOD = 0xBE + VK_OEM_2 = 0xBF + VK_OEM_3 = 0xC0 + VK_OEM_4 = 0xDB + VK_OEM_5 = 0xDC + VK_OEM_6 = 0xDD + VK_OEM_7 = 0xDE + VK_OEM_8 = 0xDF + VK_OEM_102 = 0xE2 + VK_PROCESSKEY = 0xE5 + VK_PACKET = 0xE7 + VK_ATTN = 0xF6 + VK_CRSEL = 0xF7 + VK_EXSEL = 0xF8 + VK_EREOF = 0xF9 + VK_PLAY = 0xFA + VK_ZOOM = 0xFB + VK_NONAME = 0xFC + VK_PA1 = 0xFD + VK_OEM_CLEAR = 0xFE +) + +// ScrollBar constants +const ( + SB_HORZ = 0 + SB_VERT = 1 + SB_CTL = 2 + SB_BOTH = 3 +) + +// ScrollBar commands +const ( + SB_LINEUP = 0 + SB_LINELEFT = 0 + SB_LINEDOWN = 1 + SB_LINERIGHT = 1 + SB_PAGEUP = 2 + SB_PAGELEFT = 2 + SB_PAGEDOWN = 3 + SB_PAGERIGHT = 3 + SB_THUMBPOSITION = 4 + SB_THUMBTRACK = 5 + SB_TOP = 6 + SB_LEFT = 6 + SB_BOTTOM = 7 + SB_RIGHT = 7 + SB_ENDSCROLL = 8 +) + +// [Get|Set]ScrollInfo mask constants +const ( + SIF_RANGE = 1 + SIF_PAGE = 2 + SIF_POS = 4 + SIF_DISABLENOSCROLL = 8 + SIF_TRACKPOS = 16 + SIF_ALL = SIF_RANGE + SIF_PAGE + SIF_POS + SIF_TRACKPOS +) diff --git a/v2/internal/frontend/desktop/windows/winc/w32/dwmapi.go b/v2/internal/frontend/desktop/windows/winc/w32/dwmapi.go new file mode 100644 index 000000000..56545a943 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/dwmapi.go @@ -0,0 +1,18 @@ +package w32 + +import "syscall" + +var ( + moddwmapi = syscall.NewLazyDLL("dwmapi.dll") + + procDwmSetWindowAttribute = moddwmapi.NewProc("DwmSetWindowAttribute") +) + +func DwmSetWindowAttribute(hwnd HWND, dwAttribute DWMWINDOWATTRIBUTE, pvAttribute LPCVOID, cbAttribute uint32) HRESULT { + ret, _, _ := procDwmSetWindowAttribute.Call( + hwnd, + uintptr(dwAttribute), + uintptr(pvAttribute), + uintptr(cbAttribute)) + return HRESULT(ret) +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/gdi32.go b/v2/internal/frontend/desktop/windows/winc/w32/gdi32.go new file mode 100644 index 000000000..f54c0b536 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/gdi32.go @@ -0,0 +1,525 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +import ( + "syscall" + "unsafe" +) + +var ( + modgdi32 = syscall.NewLazyDLL("gdi32.dll") + + procGetDeviceCaps = modgdi32.NewProc("GetDeviceCaps") + procDeleteObject = modgdi32.NewProc("DeleteObject") + procCreateFontIndirect = modgdi32.NewProc("CreateFontIndirectW") + procAbortDoc = modgdi32.NewProc("AbortDoc") + procBitBlt = modgdi32.NewProc("BitBlt") + procPatBlt = modgdi32.NewProc("PatBlt") + procCloseEnhMetaFile = modgdi32.NewProc("CloseEnhMetaFile") + procCopyEnhMetaFile = modgdi32.NewProc("CopyEnhMetaFileW") + procCreateBrushIndirect = modgdi32.NewProc("CreateBrushIndirect") + procCreateCompatibleDC = modgdi32.NewProc("CreateCompatibleDC") + procCreateDC = modgdi32.NewProc("CreateDCW") + procCreateDIBSection = modgdi32.NewProc("CreateDIBSection") + procCreateEnhMetaFile = modgdi32.NewProc("CreateEnhMetaFileW") + procCreateIC = modgdi32.NewProc("CreateICW") + procDeleteDC = modgdi32.NewProc("DeleteDC") + procDeleteEnhMetaFile = modgdi32.NewProc("DeleteEnhMetaFile") + procEllipse = modgdi32.NewProc("Ellipse") + procEndDoc = modgdi32.NewProc("EndDoc") + procEndPage = modgdi32.NewProc("EndPage") + procExtCreatePen = modgdi32.NewProc("ExtCreatePen") + procGetEnhMetaFile = modgdi32.NewProc("GetEnhMetaFileW") + procGetEnhMetaFileHeader = modgdi32.NewProc("GetEnhMetaFileHeader") + procGetObject = modgdi32.NewProc("GetObjectW") + procGetStockObject = modgdi32.NewProc("GetStockObject") + procGetTextExtentExPoint = modgdi32.NewProc("GetTextExtentExPointW") + procGetTextExtentPoint32 = modgdi32.NewProc("GetTextExtentPoint32W") + procGetTextMetrics = modgdi32.NewProc("GetTextMetricsW") + procLineTo = modgdi32.NewProc("LineTo") + procMoveToEx = modgdi32.NewProc("MoveToEx") + procPlayEnhMetaFile = modgdi32.NewProc("PlayEnhMetaFile") + procRectangle = modgdi32.NewProc("Rectangle") + procResetDC = modgdi32.NewProc("ResetDCW") + procSelectObject = modgdi32.NewProc("SelectObject") + procSetBkMode = modgdi32.NewProc("SetBkMode") + procSetBrushOrgEx = modgdi32.NewProc("SetBrushOrgEx") + procSetStretchBltMode = modgdi32.NewProc("SetStretchBltMode") + procSetTextColor = modgdi32.NewProc("SetTextColor") + procSetBkColor = modgdi32.NewProc("SetBkColor") + procStartDoc = modgdi32.NewProc("StartDocW") + procStartPage = modgdi32.NewProc("StartPage") + procStretchBlt = modgdi32.NewProc("StretchBlt") + procSetDIBitsToDevice = modgdi32.NewProc("SetDIBitsToDevice") + procChoosePixelFormat = modgdi32.NewProc("ChoosePixelFormat") + procDescribePixelFormat = modgdi32.NewProc("DescribePixelFormat") + procGetEnhMetaFilePixelFormat = modgdi32.NewProc("GetEnhMetaFilePixelFormat") + procGetPixelFormat = modgdi32.NewProc("GetPixelFormat") + procSetPixelFormat = modgdi32.NewProc("SetPixelFormat") + procSwapBuffers = modgdi32.NewProc("SwapBuffers") +) + +func GetDeviceCaps(hdc HDC, index int) int { + ret, _, _ := procGetDeviceCaps.Call( + uintptr(hdc), + uintptr(index)) + + return int(ret) +} + +func DeleteObject(hObject HGDIOBJ) bool { + ret, _, _ := procDeleteObject.Call( + uintptr(hObject)) + + return ret != 0 +} + +func CreateFontIndirect(logFont *LOGFONT) HFONT { + ret, _, _ := procCreateFontIndirect.Call( + uintptr(unsafe.Pointer(logFont))) + + return HFONT(ret) +} + +func AbortDoc(hdc HDC) int { + ret, _, _ := procAbortDoc.Call( + uintptr(hdc)) + + return int(ret) +} + +func BitBlt(hdcDest HDC, nXDest, nYDest, nWidth, nHeight int, hdcSrc HDC, nXSrc, nYSrc int, dwRop uint) { + ret, _, _ := procBitBlt.Call( + uintptr(hdcDest), + uintptr(nXDest), + uintptr(nYDest), + uintptr(nWidth), + uintptr(nHeight), + uintptr(hdcSrc), + uintptr(nXSrc), + uintptr(nYSrc), + uintptr(dwRop)) + + if ret == 0 { + panic("BitBlt failed") + } +} + +func PatBlt(hdc HDC, nXLeft, nYLeft, nWidth, nHeight int, dwRop uint) { + ret, _, _ := procPatBlt.Call( + uintptr(hdc), + uintptr(nXLeft), + uintptr(nYLeft), + uintptr(nWidth), + uintptr(nHeight), + uintptr(dwRop)) + + if ret == 0 { + panic("PatBlt failed") + } +} + +func CloseEnhMetaFile(hdc HDC) HENHMETAFILE { + ret, _, _ := procCloseEnhMetaFile.Call( + uintptr(hdc)) + + return HENHMETAFILE(ret) +} + +func CopyEnhMetaFile(hemfSrc HENHMETAFILE, lpszFile *uint16) HENHMETAFILE { + ret, _, _ := procCopyEnhMetaFile.Call( + uintptr(hemfSrc), + uintptr(unsafe.Pointer(lpszFile))) + + return HENHMETAFILE(ret) +} + +func CreateBrushIndirect(lplb *LOGBRUSH) HBRUSH { + ret, _, _ := procCreateBrushIndirect.Call( + uintptr(unsafe.Pointer(lplb))) + + return HBRUSH(ret) +} + +func CreateCompatibleDC(hdc HDC) HDC { + ret, _, _ := procCreateCompatibleDC.Call( + uintptr(hdc)) + + if ret == 0 { + panic("Create compatible DC failed") + } + + return HDC(ret) +} + +func CreateDC(lpszDriver, lpszDevice, lpszOutput *uint16, lpInitData *DEVMODE) HDC { + ret, _, _ := procCreateDC.Call( + uintptr(unsafe.Pointer(lpszDriver)), + uintptr(unsafe.Pointer(lpszDevice)), + uintptr(unsafe.Pointer(lpszOutput)), + uintptr(unsafe.Pointer(lpInitData))) + + return HDC(ret) +} + +func CreateDIBSection(hdc HDC, pbmi *BITMAPINFO, iUsage uint, ppvBits *unsafe.Pointer, hSection HANDLE, dwOffset uint) HBITMAP { + ret, _, _ := procCreateDIBSection.Call( + uintptr(hdc), + uintptr(unsafe.Pointer(pbmi)), + uintptr(iUsage), + uintptr(unsafe.Pointer(ppvBits)), + uintptr(hSection), + uintptr(dwOffset)) + + return HBITMAP(ret) +} + +func CreateEnhMetaFile(hdcRef HDC, lpFilename *uint16, lpRect *RECT, lpDescription *uint16) HDC { + ret, _, _ := procCreateEnhMetaFile.Call( + uintptr(hdcRef), + uintptr(unsafe.Pointer(lpFilename)), + uintptr(unsafe.Pointer(lpRect)), + uintptr(unsafe.Pointer(lpDescription))) + + return HDC(ret) +} + +func CreateIC(lpszDriver, lpszDevice, lpszOutput *uint16, lpdvmInit *DEVMODE) HDC { + ret, _, _ := procCreateIC.Call( + uintptr(unsafe.Pointer(lpszDriver)), + uintptr(unsafe.Pointer(lpszDevice)), + uintptr(unsafe.Pointer(lpszOutput)), + uintptr(unsafe.Pointer(lpdvmInit))) + + return HDC(ret) +} + +func DeleteDC(hdc HDC) bool { + ret, _, _ := procDeleteDC.Call( + uintptr(hdc)) + + return ret != 0 +} + +func DeleteEnhMetaFile(hemf HENHMETAFILE) bool { + ret, _, _ := procDeleteEnhMetaFile.Call( + uintptr(hemf)) + + return ret != 0 +} + +func Ellipse(hdc HDC, nLeftRect, nTopRect, nRightRect, nBottomRect int32) bool { + ret, _, _ := procEllipse.Call( + uintptr(hdc), + uintptr(nLeftRect), + uintptr(nTopRect), + uintptr(nRightRect), + uintptr(nBottomRect)) + + return ret != 0 +} + +func EndDoc(hdc HDC) int { + ret, _, _ := procEndDoc.Call( + uintptr(hdc)) + + return int(ret) +} + +func EndPage(hdc HDC) int { + ret, _, _ := procEndPage.Call( + uintptr(hdc)) + + return int(ret) +} + +func ExtCreatePen(dwPenStyle, dwWidth uint, lplb *LOGBRUSH, dwStyleCount uint, lpStyle *uint) HPEN { + ret, _, _ := procExtCreatePen.Call( + uintptr(dwPenStyle), + uintptr(dwWidth), + uintptr(unsafe.Pointer(lplb)), + uintptr(dwStyleCount), + uintptr(unsafe.Pointer(lpStyle))) + + return HPEN(ret) +} + +func GetEnhMetaFile(lpszMetaFile *uint16) HENHMETAFILE { + ret, _, _ := procGetEnhMetaFile.Call( + uintptr(unsafe.Pointer(lpszMetaFile))) + + return HENHMETAFILE(ret) +} + +func GetEnhMetaFileHeader(hemf HENHMETAFILE, cbBuffer uint, lpemh *ENHMETAHEADER) uint { + ret, _, _ := procGetEnhMetaFileHeader.Call( + uintptr(hemf), + uintptr(cbBuffer), + uintptr(unsafe.Pointer(lpemh))) + + return uint(ret) +} + +func GetObject(hgdiobj HGDIOBJ, cbBuffer uintptr, lpvObject unsafe.Pointer) int { + ret, _, _ := procGetObject.Call( + uintptr(hgdiobj), + uintptr(cbBuffer), + uintptr(lpvObject)) + + return int(ret) +} + +func GetStockObject(fnObject int) HGDIOBJ { + ret, _, _ := procGetDeviceCaps.Call( + uintptr(fnObject)) + + return HGDIOBJ(ret) +} + +func GetTextExtentExPoint(hdc HDC, lpszStr *uint16, cchString, nMaxExtent int, lpnFit, alpDx *int, lpSize *SIZE) bool { + ret, _, _ := procGetTextExtentExPoint.Call( + uintptr(hdc), + uintptr(unsafe.Pointer(lpszStr)), + uintptr(cchString), + uintptr(nMaxExtent), + uintptr(unsafe.Pointer(lpnFit)), + uintptr(unsafe.Pointer(alpDx)), + uintptr(unsafe.Pointer(lpSize))) + + return ret != 0 +} + +func GetTextExtentPoint32(hdc HDC, lpString *uint16, c int, lpSize *SIZE) bool { + ret, _, _ := procGetTextExtentPoint32.Call( + uintptr(hdc), + uintptr(unsafe.Pointer(lpString)), + uintptr(c), + uintptr(unsafe.Pointer(lpSize))) + + return ret != 0 +} + +func GetTextMetrics(hdc HDC, lptm *TEXTMETRIC) bool { + ret, _, _ := procGetTextMetrics.Call( + uintptr(hdc), + uintptr(unsafe.Pointer(lptm))) + + return ret != 0 +} + +func LineTo(hdc HDC, nXEnd, nYEnd int32) bool { + ret, _, _ := procLineTo.Call( + uintptr(hdc), + uintptr(nXEnd), + uintptr(nYEnd)) + + return ret != 0 +} + +func MoveToEx(hdc HDC, x, y int, lpPoint *POINT) bool { + ret, _, _ := procMoveToEx.Call( + uintptr(hdc), + uintptr(x), + uintptr(y), + uintptr(unsafe.Pointer(lpPoint))) + + return ret != 0 +} + +func PlayEnhMetaFile(hdc HDC, hemf HENHMETAFILE, lpRect *RECT) bool { + ret, _, _ := procPlayEnhMetaFile.Call( + uintptr(hdc), + uintptr(hemf), + uintptr(unsafe.Pointer(lpRect))) + + return ret != 0 +} + +func Rectangle(hdc HDC, nLeftRect, nTopRect, nRightRect, nBottomRect int32) bool { + ret, _, _ := procRectangle.Call( + uintptr(hdc), + uintptr(nLeftRect), + uintptr(nTopRect), + uintptr(nRightRect), + uintptr(nBottomRect)) + + return ret != 0 +} + +func ResetDC(hdc HDC, lpInitData *DEVMODE) HDC { + ret, _, _ := procResetDC.Call( + uintptr(hdc), + uintptr(unsafe.Pointer(lpInitData))) + + return HDC(ret) +} + +func SelectObject(hdc HDC, hgdiobj HGDIOBJ) HGDIOBJ { + ret, _, _ := procSelectObject.Call( + uintptr(hdc), + uintptr(hgdiobj)) + + if ret == 0 { + panic("SelectObject failed") + } + + return HGDIOBJ(ret) +} + +func SetBkMode(hdc HDC, iBkMode int) int { + ret, _, _ := procSetBkMode.Call( + uintptr(hdc), + uintptr(iBkMode)) + + if ret == 0 { + panic("SetBkMode failed") + } + + return int(ret) +} + +func SetBrushOrgEx(hdc HDC, nXOrg, nYOrg int, lppt *POINT) bool { + ret, _, _ := procSetBrushOrgEx.Call( + uintptr(hdc), + uintptr(nXOrg), + uintptr(nYOrg), + uintptr(unsafe.Pointer(lppt))) + + return ret != 0 +} + +func SetStretchBltMode(hdc HDC, iStretchMode int) int { + ret, _, _ := procSetStretchBltMode.Call( + uintptr(hdc), + uintptr(iStretchMode)) + + return int(ret) +} + +func SetTextColor(hdc HDC, crColor COLORREF) COLORREF { + ret, _, _ := procSetTextColor.Call( + uintptr(hdc), + uintptr(crColor)) + + if ret == CLR_INVALID { + panic("SetTextColor failed") + } + + return COLORREF(ret) +} + +func SetBkColor(hdc HDC, crColor COLORREF) COLORREF { + ret, _, _ := procSetBkColor.Call( + uintptr(hdc), + uintptr(crColor)) + + if ret == CLR_INVALID { + panic("SetBkColor failed") + } + + return COLORREF(ret) +} + +func StartDoc(hdc HDC, lpdi *DOCINFO) int { + ret, _, _ := procStartDoc.Call( + uintptr(hdc), + uintptr(unsafe.Pointer(lpdi))) + + return int(ret) +} + +func StartPage(hdc HDC) int { + ret, _, _ := procStartPage.Call( + uintptr(hdc)) + + return int(ret) +} + +func StretchBlt(hdcDest HDC, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest int, hdcSrc HDC, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc int, dwRop uint) { + ret, _, _ := procStretchBlt.Call( + uintptr(hdcDest), + uintptr(nXOriginDest), + uintptr(nYOriginDest), + uintptr(nWidthDest), + uintptr(nHeightDest), + uintptr(hdcSrc), + uintptr(nXOriginSrc), + uintptr(nYOriginSrc), + uintptr(nWidthSrc), + uintptr(nHeightSrc), + uintptr(dwRop)) + + if ret == 0 { + panic("StretchBlt failed") + } +} + +func SetDIBitsToDevice(hdc HDC, xDest, yDest, dwWidth, dwHeight, xSrc, ySrc int, uStartScan, cScanLines uint, lpvBits []byte, lpbmi *BITMAPINFO, fuColorUse uint) int { + ret, _, _ := procSetDIBitsToDevice.Call( + uintptr(hdc), + uintptr(xDest), + uintptr(yDest), + uintptr(dwWidth), + uintptr(dwHeight), + uintptr(xSrc), + uintptr(ySrc), + uintptr(uStartScan), + uintptr(cScanLines), + uintptr(unsafe.Pointer(&lpvBits[0])), + uintptr(unsafe.Pointer(lpbmi)), + uintptr(fuColorUse)) + + return int(ret) +} + +func ChoosePixelFormat(hdc HDC, pfd *PIXELFORMATDESCRIPTOR) int { + ret, _, _ := procChoosePixelFormat.Call( + uintptr(hdc), + uintptr(unsafe.Pointer(pfd)), + ) + return int(ret) +} + +func DescribePixelFormat(hdc HDC, iPixelFormat int, nBytes uint, pfd *PIXELFORMATDESCRIPTOR) int { + ret, _, _ := procDescribePixelFormat.Call( + uintptr(hdc), + uintptr(iPixelFormat), + uintptr(nBytes), + uintptr(unsafe.Pointer(pfd)), + ) + return int(ret) +} + +func GetEnhMetaFilePixelFormat(hemf HENHMETAFILE, cbBuffer uint32, pfd *PIXELFORMATDESCRIPTOR) uint { + ret, _, _ := procGetEnhMetaFilePixelFormat.Call( + uintptr(hemf), + uintptr(cbBuffer), + uintptr(unsafe.Pointer(pfd)), + ) + return uint(ret) +} + +func GetPixelFormat(hdc HDC) int { + ret, _, _ := procGetPixelFormat.Call( + uintptr(hdc), + ) + return int(ret) +} + +func SetPixelFormat(hdc HDC, iPixelFormat int, pfd *PIXELFORMATDESCRIPTOR) bool { + ret, _, _ := procSetPixelFormat.Call( + uintptr(hdc), + uintptr(iPixelFormat), + uintptr(unsafe.Pointer(pfd)), + ) + return ret == TRUE +} + +func SwapBuffers(hdc HDC) bool { + ret, _, _ := procSwapBuffers.Call(uintptr(hdc)) + return ret == TRUE +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/gdiplus.go b/v2/internal/frontend/desktop/windows/winc/w32/gdiplus.go new file mode 100644 index 000000000..a8d7daffa --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/gdiplus.go @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +import ( + "errors" + "fmt" + "syscall" + "unsafe" +) + +const ( + Ok = 0 + GenericError = 1 + InvalidParameter = 2 + OutOfMemory = 3 + ObjectBusy = 4 + InsufficientBuffer = 5 + NotImplemented = 6 + Win32Error = 7 + WrongState = 8 + Aborted = 9 + FileNotFound = 10 + ValueOverflow = 11 + AccessDenied = 12 + UnknownImageFormat = 13 + FontFamilyNotFound = 14 + FontStyleNotFound = 15 + NotTrueTypeFont = 16 + UnsupportedGdiplusVersion = 17 + GdiplusNotInitialized = 18 + PropertyNotFound = 19 + PropertyNotSupported = 20 + ProfileNotFound = 21 +) + +func GetGpStatus(s int32) string { + switch s { + case Ok: + return "Ok" + case GenericError: + return "GenericError" + case InvalidParameter: + return "InvalidParameter" + case OutOfMemory: + return "OutOfMemory" + case ObjectBusy: + return "ObjectBusy" + case InsufficientBuffer: + return "InsufficientBuffer" + case NotImplemented: + return "NotImplemented" + case Win32Error: + return "Win32Error" + case WrongState: + return "WrongState" + case Aborted: + return "Aborted" + case FileNotFound: + return "FileNotFound" + case ValueOverflow: + return "ValueOverflow" + case AccessDenied: + return "AccessDenied" + case UnknownImageFormat: + return "UnknownImageFormat" + case FontFamilyNotFound: + return "FontFamilyNotFound" + case FontStyleNotFound: + return "FontStyleNotFound" + case NotTrueTypeFont: + return "NotTrueTypeFont" + case UnsupportedGdiplusVersion: + return "UnsupportedGdiplusVersion" + case GdiplusNotInitialized: + return "GdiplusNotInitialized" + case PropertyNotFound: + return "PropertyNotFound" + case PropertyNotSupported: + return "PropertyNotSupported" + case ProfileNotFound: + return "ProfileNotFound" + } + return "Unknown Status Value" +} + +var ( + token uintptr + + modgdiplus = syscall.NewLazyDLL("gdiplus.dll") + + procGdipCreateBitmapFromFile = modgdiplus.NewProc("GdipCreateBitmapFromFile") + procGdipCreateBitmapFromHBITMAP = modgdiplus.NewProc("GdipCreateBitmapFromHBITMAP") + procGdipCreateHBITMAPFromBitmap = modgdiplus.NewProc("GdipCreateHBITMAPFromBitmap") + procGdipCreateBitmapFromResource = modgdiplus.NewProc("GdipCreateBitmapFromResource") + procGdipCreateBitmapFromStream = modgdiplus.NewProc("GdipCreateBitmapFromStream") + procGdipDisposeImage = modgdiplus.NewProc("GdipDisposeImage") + procGdiplusShutdown = modgdiplus.NewProc("GdiplusShutdown") + procGdiplusStartup = modgdiplus.NewProc("GdiplusStartup") +) + +func GdipCreateBitmapFromFile(filename string) (*uintptr, error) { + var bitmap *uintptr + ret, _, _ := procGdipCreateBitmapFromFile.Call( + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(filename))), + uintptr(unsafe.Pointer(&bitmap))) + + if ret != Ok { + return nil, errors.New(fmt.Sprintf("GdipCreateBitmapFromFile failed with status '%s' for file '%s'", GetGpStatus(int32(ret)), filename)) + } + + return bitmap, nil +} + +func GdipCreateBitmapFromResource(instance HINSTANCE, resId *uint16) (*uintptr, error) { + var bitmap *uintptr + ret, _, _ := procGdipCreateBitmapFromResource.Call( + uintptr(instance), + uintptr(unsafe.Pointer(resId)), + uintptr(unsafe.Pointer(&bitmap))) + + if ret != Ok { + return nil, errors.New(fmt.Sprintf("GdiCreateBitmapFromResource failed with status '%s'", GetGpStatus(int32(ret)))) + } + + return bitmap, nil +} + +func GdipCreateBitmapFromStream(stream *IStream) (*uintptr, error) { + var bitmap *uintptr + ret, _, _ := procGdipCreateBitmapFromStream.Call( + uintptr(unsafe.Pointer(stream)), + uintptr(unsafe.Pointer(&bitmap))) + + if ret != Ok { + return nil, errors.New(fmt.Sprintf("GdipCreateBitmapFromStream failed with status '%s'", GetGpStatus(int32(ret)))) + } + + return bitmap, nil +} + +func GdipCreateHBITMAPFromBitmap(bitmap *uintptr, background uint32) (HBITMAP, error) { + var hbitmap HBITMAP + ret, _, _ := procGdipCreateHBITMAPFromBitmap.Call( + uintptr(unsafe.Pointer(bitmap)), + uintptr(unsafe.Pointer(&hbitmap)), + uintptr(background)) + + if ret != Ok { + return 0, errors.New(fmt.Sprintf("GdipCreateHBITMAPFromBitmap failed with status '%s'", GetGpStatus(int32(ret)))) + } + + return hbitmap, nil +} + +func GdipDisposeImage(image *uintptr) { + procGdipDisposeImage.Call(uintptr(unsafe.Pointer(image))) +} + +func GdiplusShutdown() { + procGdiplusShutdown.Call(token) +} + +func GdiplusStartup(input *GdiplusStartupInput, output *GdiplusStartupOutput) { + ret, _, _ := procGdiplusStartup.Call( + uintptr(unsafe.Pointer(&token)), + uintptr(unsafe.Pointer(input)), + uintptr(unsafe.Pointer(output))) + + if ret != Ok { + panic("GdiplusStartup failed with status " + GetGpStatus(int32(ret))) + } +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/idispatch.go b/v2/internal/frontend/desktop/windows/winc/w32/idispatch.go new file mode 100644 index 000000000..2396c9c10 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/idispatch.go @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +import ( + "unsafe" +) + +type pIDispatchVtbl struct { + pQueryInterface uintptr + pAddRef uintptr + pRelease uintptr + pGetTypeInfoCount uintptr + pGetTypeInfo uintptr + pGetIDsOfNames uintptr + pInvoke uintptr +} + +type IDispatch struct { + lpVtbl *pIDispatchVtbl +} + +func (this *IDispatch) QueryInterface(id *GUID) *IDispatch { + return ComQueryInterface((*IUnknown)(unsafe.Pointer(this)), id) +} + +func (this *IDispatch) AddRef() int32 { + return ComAddRef((*IUnknown)(unsafe.Pointer(this))) +} + +func (this *IDispatch) Release() int32 { + return ComRelease((*IUnknown)(unsafe.Pointer(this))) +} + +func (this *IDispatch) GetIDsOfName(names []string) []int32 { + return ComGetIDsOfName(this, names) +} + +func (this *IDispatch) Invoke(dispid int32, dispatch int16, params ...interface{}) *VARIANT { + return ComInvoke(this, dispid, dispatch, params...) +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/istream.go b/v2/internal/frontend/desktop/windows/winc/w32/istream.go new file mode 100644 index 000000000..a937c7ac8 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/istream.go @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 Tad Vizbaras. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +import ( + "unsafe" +) + +type pIStreamVtbl struct { + pQueryInterface uintptr + pAddRef uintptr + pRelease uintptr +} + +type IStream struct { + lpVtbl *pIStreamVtbl +} + +func (this *IStream) QueryInterface(id *GUID) *IDispatch { + return ComQueryInterface((*IUnknown)(unsafe.Pointer(this)), id) +} + +func (this *IStream) AddRef() int32 { + return ComAddRef((*IUnknown)(unsafe.Pointer(this))) +} + +func (this *IStream) Release() int32 { + return ComRelease((*IUnknown)(unsafe.Pointer(this))) +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/iunknown.go b/v2/internal/frontend/desktop/windows/winc/w32/iunknown.go new file mode 100644 index 000000000..388cf10ab --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/iunknown.go @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 Tad Vizbaras. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +type pIUnknownVtbl struct { + pQueryInterface uintptr + pAddRef uintptr + pRelease uintptr +} + +type IUnknown struct { + lpVtbl *pIUnknownVtbl +} + +func (this *IUnknown) QueryInterface(id *GUID) *IDispatch { + return ComQueryInterface(this, id) +} + +func (this *IUnknown) AddRef() int32 { + return ComAddRef(this) +} + +func (this *IUnknown) Release() int32 { + return ComRelease(this) +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/kernel32.go b/v2/internal/frontend/desktop/windows/winc/w32/kernel32.go new file mode 100644 index 000000000..66a9b5f64 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/kernel32.go @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2019 Tad Vizbaras. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +import ( + "syscall" + "unsafe" +) + +var ( + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + + procGetModuleHandle = modkernel32.NewProc("GetModuleHandleW") + procMulDiv = modkernel32.NewProc("MulDiv") + procGetConsoleWindow = modkernel32.NewProc("GetConsoleWindow") + procGetCurrentThread = modkernel32.NewProc("GetCurrentThread") + procGetCurrentThreadId = modkernel32.NewProc("GetCurrentThreadId") + procGetLogicalDrives = modkernel32.NewProc("GetLogicalDrives") + procGetLogicalDriveStrings = modkernel32.NewProc("GetLogicalDriveStringsW") + procGetUserDefaultLCID = modkernel32.NewProc("GetUserDefaultLCID") + procLstrlen = modkernel32.NewProc("lstrlenW") + procLstrcpy = modkernel32.NewProc("lstrcpyW") + procGlobalAlloc = modkernel32.NewProc("GlobalAlloc") + procGlobalFree = modkernel32.NewProc("GlobalFree") + procGlobalLock = modkernel32.NewProc("GlobalLock") + procGlobalUnlock = modkernel32.NewProc("GlobalUnlock") + procMoveMemory = modkernel32.NewProc("RtlMoveMemory") + procFindResource = modkernel32.NewProc("FindResourceW") + procSizeofResource = modkernel32.NewProc("SizeofResource") + procLockResource = modkernel32.NewProc("LockResource") + procLoadResource = modkernel32.NewProc("LoadResource") + procGetLastError = modkernel32.NewProc("GetLastError") + procOpenProcess = modkernel32.NewProc("OpenProcess") + procTerminateProcess = modkernel32.NewProc("TerminateProcess") + procCloseHandle = modkernel32.NewProc("CloseHandle") + procCreateToolhelp32Snapshot = modkernel32.NewProc("CreateToolhelp32Snapshot") + procModule32First = modkernel32.NewProc("Module32FirstW") + procModule32Next = modkernel32.NewProc("Module32NextW") + procGetSystemTimes = modkernel32.NewProc("GetSystemTimes") + procGetConsoleScreenBufferInfo = modkernel32.NewProc("GetConsoleScreenBufferInfo") + procSetConsoleTextAttribute = modkernel32.NewProc("SetConsoleTextAttribute") + procGetDiskFreeSpaceEx = modkernel32.NewProc("GetDiskFreeSpaceExW") + procGetProcessTimes = modkernel32.NewProc("GetProcessTimes") + procSetSystemTime = modkernel32.NewProc("SetSystemTime") + procGetSystemTime = modkernel32.NewProc("GetSystemTime") +) + +func GetModuleHandle(modulename string) HINSTANCE { + var mn uintptr + if modulename == "" { + mn = 0 + } else { + mn = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(modulename))) + } + ret, _, _ := procGetModuleHandle.Call(mn) + return HINSTANCE(ret) +} + +func MulDiv(number, numerator, denominator int) int { + ret, _, _ := procMulDiv.Call( + uintptr(number), + uintptr(numerator), + uintptr(denominator)) + + return int(ret) +} + +func GetConsoleWindow() HWND { + ret, _, _ := procGetConsoleWindow.Call() + + return HWND(ret) +} + +func GetCurrentThread() HANDLE { + ret, _, _ := procGetCurrentThread.Call() + + return HANDLE(ret) +} + +func GetCurrentThreadId() HANDLE { + ret, _, _ := procGetCurrentThreadId.Call() + + return HANDLE(ret) +} + +func GetLogicalDrives() uint32 { + ret, _, _ := procGetLogicalDrives.Call() + + return uint32(ret) +} + +func GetUserDefaultLCID() uint32 { + ret, _, _ := procGetUserDefaultLCID.Call() + + return uint32(ret) +} + +func Lstrlen(lpString *uint16) int { + ret, _, _ := procLstrlen.Call(uintptr(unsafe.Pointer(lpString))) + + return int(ret) +} + +func Lstrcpy(buf []uint16, lpString *uint16) { + procLstrcpy.Call( + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(lpString))) +} + +func GlobalAlloc(uFlags uint, dwBytes uint32) HGLOBAL { + ret, _, _ := procGlobalAlloc.Call( + uintptr(uFlags), + uintptr(dwBytes)) + + if ret == 0 { + panic("GlobalAlloc failed") + } + + return HGLOBAL(ret) +} + +func GlobalFree(hMem HGLOBAL) { + ret, _, _ := procGlobalFree.Call(uintptr(hMem)) + + if ret != 0 { + panic("GlobalFree failed") + } +} + +func GlobalLock(hMem HGLOBAL) unsafe.Pointer { + ret, _, _ := procGlobalLock.Call(uintptr(hMem)) + + if ret == 0 { + panic("GlobalLock failed") + } + + return unsafe.Pointer(ret) +} + +func GlobalUnlock(hMem HGLOBAL) bool { + ret, _, _ := procGlobalUnlock.Call(uintptr(hMem)) + + return ret != 0 +} + +func MoveMemory(destination, source unsafe.Pointer, length uint32) { + procMoveMemory.Call( + uintptr(unsafe.Pointer(destination)), + uintptr(source), + uintptr(length)) +} + +func FindResource(hModule HMODULE, lpName, lpType *uint16) (HRSRC, error) { + ret, _, _ := procFindResource.Call( + uintptr(hModule), + uintptr(unsafe.Pointer(lpName)), + uintptr(unsafe.Pointer(lpType))) + + if ret == 0 { + return 0, syscall.GetLastError() + } + + return HRSRC(ret), nil +} + +func SizeofResource(hModule HMODULE, hResInfo HRSRC) uint32 { + ret, _, _ := procSizeofResource.Call( + uintptr(hModule), + uintptr(hResInfo)) + + if ret == 0 { + panic("SizeofResource failed") + } + + return uint32(ret) +} + +func LockResource(hResData HGLOBAL) unsafe.Pointer { + ret, _, _ := procLockResource.Call(uintptr(hResData)) + + if ret == 0 { + panic("LockResource failed") + } + + return unsafe.Pointer(ret) +} + +func LoadResource(hModule HMODULE, hResInfo HRSRC) HGLOBAL { + ret, _, _ := procLoadResource.Call( + uintptr(hModule), + uintptr(hResInfo)) + + if ret == 0 { + panic("LoadResource failed") + } + + return HGLOBAL(ret) +} + +func GetLastError() uint32 { + ret, _, _ := procGetLastError.Call() + return uint32(ret) +} + +func OpenProcess(desiredAccess uint32, inheritHandle bool, processId uint32) HANDLE { + inherit := 0 + if inheritHandle { + inherit = 1 + } + + ret, _, _ := procOpenProcess.Call( + uintptr(desiredAccess), + uintptr(inherit), + uintptr(processId)) + return HANDLE(ret) +} + +func TerminateProcess(hProcess HANDLE, uExitCode uint) bool { + ret, _, _ := procTerminateProcess.Call( + uintptr(hProcess), + uintptr(uExitCode)) + return ret != 0 +} + +func CloseHandle(object HANDLE) bool { + ret, _, _ := procCloseHandle.Call( + uintptr(object)) + return ret != 0 +} + +func CreateToolhelp32Snapshot(flags, processId uint32) HANDLE { + ret, _, _ := procCreateToolhelp32Snapshot.Call( + uintptr(flags), + uintptr(processId)) + + if ret <= 0 { + return HANDLE(0) + } + + return HANDLE(ret) +} + +func Module32First(snapshot HANDLE, me *MODULEENTRY32) bool { + ret, _, _ := procModule32First.Call( + uintptr(snapshot), + uintptr(unsafe.Pointer(me))) + + return ret != 0 +} + +func Module32Next(snapshot HANDLE, me *MODULEENTRY32) bool { + ret, _, _ := procModule32Next.Call( + uintptr(snapshot), + uintptr(unsafe.Pointer(me))) + + return ret != 0 +} + +func GetSystemTimes(lpIdleTime, lpKernelTime, lpUserTime *FILETIME) bool { + ret, _, _ := procGetSystemTimes.Call( + uintptr(unsafe.Pointer(lpIdleTime)), + uintptr(unsafe.Pointer(lpKernelTime)), + uintptr(unsafe.Pointer(lpUserTime))) + + return ret != 0 +} + +func GetProcessTimes(hProcess HANDLE, lpCreationTime, lpExitTime, lpKernelTime, lpUserTime *FILETIME) bool { + ret, _, _ := procGetProcessTimes.Call( + uintptr(hProcess), + uintptr(unsafe.Pointer(lpCreationTime)), + uintptr(unsafe.Pointer(lpExitTime)), + uintptr(unsafe.Pointer(lpKernelTime)), + uintptr(unsafe.Pointer(lpUserTime))) + + return ret != 0 +} + +func GetConsoleScreenBufferInfo(hConsoleOutput HANDLE) *CONSOLE_SCREEN_BUFFER_INFO { + var csbi CONSOLE_SCREEN_BUFFER_INFO + ret, _, _ := procGetConsoleScreenBufferInfo.Call( + uintptr(hConsoleOutput), + uintptr(unsafe.Pointer(&csbi))) + if ret == 0 { + return nil + } + return &csbi +} + +func SetConsoleTextAttribute(hConsoleOutput HANDLE, wAttributes uint16) bool { + ret, _, _ := procSetConsoleTextAttribute.Call( + uintptr(hConsoleOutput), + uintptr(wAttributes)) + return ret != 0 +} + +func GetDiskFreeSpaceEx(dirName string) (r bool, + freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes uint64) { + ret, _, _ := procGetDiskFreeSpaceEx.Call( + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(dirName))), + uintptr(unsafe.Pointer(&freeBytesAvailable)), + uintptr(unsafe.Pointer(&totalNumberOfBytes)), + uintptr(unsafe.Pointer(&totalNumberOfFreeBytes))) + return ret != 0, + freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes +} + +func GetSystemTime() *SYSTEMTIME { + var time SYSTEMTIME + procGetSystemTime.Call( + uintptr(unsafe.Pointer(&time))) + return &time +} + +func SetSystemTime(time *SYSTEMTIME) bool { + ret, _, _ := procSetSystemTime.Call( + uintptr(unsafe.Pointer(time))) + return ret != 0 +} + +func GetLogicalDriveStrings(nBufferLength uint32, lpBuffer *uint16) uint32 { + ret, _, _ := procGetLogicalDriveStrings.Call( + uintptr(nBufferLength), + uintptr(unsafe.Pointer(lpBuffer)), + 0) + + return uint32(ret) +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/ole32.go b/v2/internal/frontend/desktop/windows/winc/w32/ole32.go new file mode 100644 index 000000000..d5cc0df45 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/ole32.go @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2019 Tad Vizbaras. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +import ( + "syscall" + "unsafe" +) + +var ( + modole32 = syscall.NewLazyDLL("ole32.dll") + + procCoInitializeEx = modole32.NewProc("CoInitializeEx") + procCoInitialize = modole32.NewProc("CoInitialize") + procCoUninitialize = modole32.NewProc("CoUninitialize") + procCreateStreamOnHGlobal = modole32.NewProc("CreateStreamOnHGlobal") +) + +func CoInitializeEx(coInit uintptr) HRESULT { + ret, _, _ := procCoInitializeEx.Call( + 0, + coInit) + + switch uint32(ret) { + case E_INVALIDARG: + panic("CoInitializeEx failed with E_INVALIDARG") + case E_OUTOFMEMORY: + panic("CoInitializeEx failed with E_OUTOFMEMORY") + case E_UNEXPECTED: + panic("CoInitializeEx failed with E_UNEXPECTED") + } + + return HRESULT(ret) +} + +func CoInitialize() { + procCoInitialize.Call(0) +} + +func CoUninitialize() { + procCoUninitialize.Call() +} + +func CreateStreamOnHGlobal(hGlobal HGLOBAL, fDeleteOnRelease bool) *IStream { + stream := new(IStream) + ret, _, _ := procCreateStreamOnHGlobal.Call( + uintptr(hGlobal), + uintptr(BoolToBOOL(fDeleteOnRelease)), + uintptr(unsafe.Pointer(&stream))) + + switch uint32(ret) { + case E_INVALIDARG: + panic("CreateStreamOnHGlobal failed with E_INVALIDARG") + case E_OUTOFMEMORY: + panic("CreateStreamOnHGlobal failed with E_OUTOFMEMORY") + case E_UNEXPECTED: + panic("CreateStreamOnHGlobal failed with E_UNEXPECTED") + } + + return stream +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/oleaut32.go b/v2/internal/frontend/desktop/windows/winc/w32/oleaut32.go new file mode 100644 index 000000000..35cc8833d --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/oleaut32.go @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 Tad Vizbaras. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +import ( + "syscall" + "unsafe" +) + +var ( + modoleaut32 = syscall.NewLazyDLL("oleaut32") + + procVariantInit = modoleaut32.NewProc("VariantInit") + procSysAllocString = modoleaut32.NewProc("SysAllocString") + procSysFreeString = modoleaut32.NewProc("SysFreeString") + procSysStringLen = modoleaut32.NewProc("SysStringLen") + procCreateDispTypeInfo = modoleaut32.NewProc("CreateDispTypeInfo") + procCreateStdDispatch = modoleaut32.NewProc("CreateStdDispatch") +) + +func VariantInit(v *VARIANT) { + hr, _, _ := procVariantInit.Call(uintptr(unsafe.Pointer(v))) + if hr != 0 { + panic("Invoke VariantInit error.") + } + return +} + +func SysAllocString(v string) (ss *int16) { + pss, _, _ := procSysAllocString.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v)))) + ss = (*int16)(unsafe.Pointer(pss)) + return +} + +func SysFreeString(v *int16) { + hr, _, _ := procSysFreeString.Call(uintptr(unsafe.Pointer(v))) + if hr != 0 { + panic("Invoke SysFreeString error.") + } + return +} + +func SysStringLen(v *int16) uint { + l, _, _ := procSysStringLen.Call(uintptr(unsafe.Pointer(v))) + return uint(l) +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/shcore.go b/v2/internal/frontend/desktop/windows/winc/w32/shcore.go new file mode 100644 index 000000000..1d23ac1fe --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/shcore.go @@ -0,0 +1,27 @@ +package w32 + +import ( + "syscall" + "unsafe" +) + +var ( + modshcore = syscall.NewLazyDLL("shcore.dll") + + procGetDpiForMonitor = modshcore.NewProc("GetDpiForMonitor") +) + +func HasGetDPIForMonitorFunc() bool { + err := procGetDpiForMonitor.Find() + return err == nil +} + +func GetDPIForMonitor(hmonitor HMONITOR, dpiType MONITOR_DPI_TYPE, dpiX *UINT, dpiY *UINT) uintptr { + ret, _, _ := procGetDpiForMonitor.Call( + hmonitor, + uintptr(dpiType), + uintptr(unsafe.Pointer(dpiX)), + uintptr(unsafe.Pointer(dpiY))) + + return ret +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/shell32.go b/v2/internal/frontend/desktop/windows/winc/w32/shell32.go new file mode 100644 index 000000000..4584a959e --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/shell32.go @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2019 Tad Vizbaras. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +import ( + "errors" + "fmt" + "syscall" + "unsafe" +) + +type CSIDL uint32 + +const ( + CSIDL_DESKTOP = 0x00 + CSIDL_INTERNET = 0x01 + CSIDL_PROGRAMS = 0x02 + CSIDL_CONTROLS = 0x03 + CSIDL_PRINTERS = 0x04 + CSIDL_PERSONAL = 0x05 + CSIDL_FAVORITES = 0x06 + CSIDL_STARTUP = 0x07 + CSIDL_RECENT = 0x08 + CSIDL_SENDTO = 0x09 + CSIDL_BITBUCKET = 0x0A + CSIDL_STARTMENU = 0x0B + CSIDL_MYDOCUMENTS = 0x0C + CSIDL_MYMUSIC = 0x0D + CSIDL_MYVIDEO = 0x0E + CSIDL_DESKTOPDIRECTORY = 0x10 + CSIDL_DRIVES = 0x11 + CSIDL_NETWORK = 0x12 + CSIDL_NETHOOD = 0x13 + CSIDL_FONTS = 0x14 + CSIDL_TEMPLATES = 0x15 + CSIDL_COMMON_STARTMENU = 0x16 + CSIDL_COMMON_PROGRAMS = 0x17 + CSIDL_COMMON_STARTUP = 0x18 + CSIDL_COMMON_DESKTOPDIRECTORY = 0x19 + CSIDL_APPDATA = 0x1A + CSIDL_PRINTHOOD = 0x1B + CSIDL_LOCAL_APPDATA = 0x1C + CSIDL_ALTSTARTUP = 0x1D + CSIDL_COMMON_ALTSTARTUP = 0x1E + CSIDL_COMMON_FAVORITES = 0x1F + CSIDL_INTERNET_CACHE = 0x20 + CSIDL_COOKIES = 0x21 + CSIDL_HISTORY = 0x22 + CSIDL_COMMON_APPDATA = 0x23 + CSIDL_WINDOWS = 0x24 + CSIDL_SYSTEM = 0x25 + CSIDL_PROGRAM_FILES = 0x26 + CSIDL_MYPICTURES = 0x27 + CSIDL_PROFILE = 0x28 + CSIDL_SYSTEMX86 = 0x29 + CSIDL_PROGRAM_FILESX86 = 0x2A + CSIDL_PROGRAM_FILES_COMMON = 0x2B + CSIDL_PROGRAM_FILES_COMMONX86 = 0x2C + CSIDL_COMMON_TEMPLATES = 0x2D + CSIDL_COMMON_DOCUMENTS = 0x2E + CSIDL_COMMON_ADMINTOOLS = 0x2F + CSIDL_ADMINTOOLS = 0x30 + CSIDL_CONNECTIONS = 0x31 + CSIDL_COMMON_MUSIC = 0x35 + CSIDL_COMMON_PICTURES = 0x36 + CSIDL_COMMON_VIDEO = 0x37 + CSIDL_RESOURCES = 0x38 + CSIDL_RESOURCES_LOCALIZED = 0x39 + CSIDL_COMMON_OEM_LINKS = 0x3A + CSIDL_CDBURN_AREA = 0x3B + CSIDL_COMPUTERSNEARME = 0x3D + CSIDL_FLAG_CREATE = 0x8000 + CSIDL_FLAG_DONT_VERIFY = 0x4000 + CSIDL_FLAG_NO_ALIAS = 0x1000 + CSIDL_FLAG_PER_USER_INIT = 0x8000 + CSIDL_FLAG_MASK = 0xFF00 +) + +var ( + modshell32 = syscall.NewLazyDLL("shell32.dll") + + procSHBrowseForFolder = modshell32.NewProc("SHBrowseForFolderW") + procSHGetPathFromIDList = modshell32.NewProc("SHGetPathFromIDListW") + procDragAcceptFiles = modshell32.NewProc("DragAcceptFiles") + procDragQueryFile = modshell32.NewProc("DragQueryFileW") + procDragQueryPoint = modshell32.NewProc("DragQueryPoint") + procDragFinish = modshell32.NewProc("DragFinish") + procShellExecute = modshell32.NewProc("ShellExecuteW") + procExtractIcon = modshell32.NewProc("ExtractIconW") + procGetSpecialFolderPath = modshell32.NewProc("SHGetSpecialFolderPathW") +) + +func SHBrowseForFolder(bi *BROWSEINFO) uintptr { + ret, _, _ := procSHBrowseForFolder.Call(uintptr(unsafe.Pointer(bi))) + + return ret +} + +func SHGetPathFromIDList(idl uintptr) string { + buf := make([]uint16, 1024) + procSHGetPathFromIDList.Call( + idl, + uintptr(unsafe.Pointer(&buf[0]))) + + return syscall.UTF16ToString(buf) +} + +func DragAcceptFiles(hwnd HWND, accept bool) { + procDragAcceptFiles.Call( + uintptr(hwnd), + uintptr(BoolToBOOL(accept))) +} + +func DragQueryFile(hDrop HDROP, iFile uint) (fileName string, fileCount uint) { + ret, _, _ := procDragQueryFile.Call( + uintptr(hDrop), + uintptr(iFile), + 0, + 0) + + fileCount = uint(ret) + + if iFile != 0xFFFFFFFF { + buf := make([]uint16, fileCount+1) + + ret, _, _ := procDragQueryFile.Call( + uintptr(hDrop), + uintptr(iFile), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(fileCount+1)) + + if ret == 0 { + panic("Invoke DragQueryFile error.") + } + + fileName = syscall.UTF16ToString(buf) + } + + return +} + +func DragQueryPoint(hDrop HDROP) (x, y int, isClientArea bool) { + var pt POINT + ret, _, _ := procDragQueryPoint.Call( + uintptr(hDrop), + uintptr(unsafe.Pointer(&pt))) + + return int(pt.X), int(pt.Y), (ret == 1) +} + +func DragFinish(hDrop HDROP) { + procDragFinish.Call(uintptr(hDrop)) +} + +func ShellExecute(hwnd HWND, lpOperation, lpFile, lpParameters, lpDirectory string, nShowCmd int) error { + var op, param, directory uintptr + if len(lpOperation) != 0 { + op = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpOperation))) + } + if len(lpParameters) != 0 { + param = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpParameters))) + } + if len(lpDirectory) != 0 { + directory = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpDirectory))) + } + + ret, _, _ := procShellExecute.Call( + uintptr(hwnd), + op, + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpFile))), + param, + directory, + uintptr(nShowCmd)) + + errorMsg := "" + if ret != 0 && ret <= 32 { + switch int(ret) { + case ERROR_FILE_NOT_FOUND: + errorMsg = "The specified file was not found." + case ERROR_PATH_NOT_FOUND: + errorMsg = "The specified path was not found." + case ERROR_BAD_FORMAT: + errorMsg = "The .exe file is invalid (non-Win32 .exe or error in .exe image)." + case SE_ERR_ACCESSDENIED: + errorMsg = "The operating system denied access to the specified file." + case SE_ERR_ASSOCINCOMPLETE: + errorMsg = "The file name association is incomplete or invalid." + case SE_ERR_DDEBUSY: + errorMsg = "The DDE transaction could not be completed because other DDE transactions were being processed." + case SE_ERR_DDEFAIL: + errorMsg = "The DDE transaction failed." + case SE_ERR_DDETIMEOUT: + errorMsg = "The DDE transaction could not be completed because the request timed out." + case SE_ERR_DLLNOTFOUND: + errorMsg = "The specified DLL was not found." + case SE_ERR_NOASSOC: + errorMsg = "There is no application associated with the given file name extension. This error will also be returned if you attempt to print a file that is not printable." + case SE_ERR_OOM: + errorMsg = "There was not enough memory to complete the operation." + case SE_ERR_SHARE: + errorMsg = "A sharing violation occurred." + default: + errorMsg = fmt.Sprintf("Unknown error occurred with error code %v", ret) + } + } else { + return nil + } + + return errors.New(errorMsg) +} + +func ExtractIcon(lpszExeFileName string, nIconIndex int) HICON { + ret, _, _ := procExtractIcon.Call( + 0, + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpszExeFileName))), + uintptr(nIconIndex)) + + return HICON(ret) +} + +func SHGetSpecialFolderPath(hwndOwner HWND, lpszPath *uint16, csidl CSIDL, fCreate bool) bool { + ret, _, _ := procGetSpecialFolderPath.Call( + uintptr(hwndOwner), + uintptr(unsafe.Pointer(lpszPath)), + uintptr(csidl), + uintptr(BoolToBOOL(fCreate)), + 0, + 0) + + return ret != 0 +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/shlwapi.go b/v2/internal/frontend/desktop/windows/winc/w32/shlwapi.go new file mode 100644 index 000000000..eb7aaeea6 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/shlwapi.go @@ -0,0 +1,24 @@ +package w32 + +import ( + "syscall" + "unsafe" +) + +var ( + modshlwapi = syscall.NewLazyDLL("shlwapi.dll") + + procSHCreateMemStream = modshlwapi.NewProc("SHCreateMemStream") +) + +func SHCreateMemStream(data []byte) (uintptr, error) { + ret, _, err := procSHCreateMemStream.Call( + uintptr(unsafe.Pointer(&data[0])), + uintptr(len(data)), + ) + if ret == 0 { + return 0, err + } + + return ret, nil +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/toolbar.go b/v2/internal/frontend/desktop/windows/winc/w32/toolbar.go new file mode 100644 index 000000000..9c0c9ffee --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/toolbar.go @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2019 Tad Vizbaras. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +// ToolBar messages +const ( + TB_ENABLEBUTTON = WM_USER + 1 + TB_CHECKBUTTON = WM_USER + 2 + TB_PRESSBUTTON = WM_USER + 3 + TB_HIDEBUTTON = WM_USER + 4 + TB_INDETERMINATE = WM_USER + 5 + TB_MARKBUTTON = WM_USER + 6 + TB_ISBUTTONENABLED = WM_USER + 9 + TB_ISBUTTONCHECKED = WM_USER + 10 + TB_ISBUTTONPRESSED = WM_USER + 11 + TB_ISBUTTONHIDDEN = WM_USER + 12 + TB_ISBUTTONINDETERMINATE = WM_USER + 13 + TB_ISBUTTONHIGHLIGHTED = WM_USER + 14 + TB_SETSTATE = WM_USER + 17 + TB_GETSTATE = WM_USER + 18 + TB_ADDBITMAP = WM_USER + 19 + TB_DELETEBUTTON = WM_USER + 22 + TB_GETBUTTON = WM_USER + 23 + TB_BUTTONCOUNT = WM_USER + 24 + TB_COMMANDTOINDEX = WM_USER + 25 + TB_SAVERESTORE = WM_USER + 76 + TB_CUSTOMIZE = WM_USER + 27 + TB_ADDSTRING = WM_USER + 77 + TB_GETITEMRECT = WM_USER + 29 + TB_BUTTONSTRUCTSIZE = WM_USER + 30 + TB_SETBUTTONSIZE = WM_USER + 31 + TB_SETBITMAPSIZE = WM_USER + 32 + TB_AUTOSIZE = WM_USER + 33 + TB_GETTOOLTIPS = WM_USER + 35 + TB_SETTOOLTIPS = WM_USER + 36 + TB_SETPARENT = WM_USER + 37 + TB_SETROWS = WM_USER + 39 + TB_GETROWS = WM_USER + 40 + TB_GETBITMAPFLAGS = WM_USER + 41 + TB_SETCMDID = WM_USER + 42 + TB_CHANGEBITMAP = WM_USER + 43 + TB_GETBITMAP = WM_USER + 44 + TB_GETBUTTONTEXT = WM_USER + 75 + TB_REPLACEBITMAP = WM_USER + 46 + TB_GETBUTTONSIZE = WM_USER + 58 + TB_SETBUTTONWIDTH = WM_USER + 59 + TB_SETINDENT = WM_USER + 47 + TB_SETIMAGELIST = WM_USER + 48 + TB_GETIMAGELIST = WM_USER + 49 + TB_LOADIMAGES = WM_USER + 50 + TB_GETRECT = WM_USER + 51 + TB_SETHOTIMAGELIST = WM_USER + 52 + TB_GETHOTIMAGELIST = WM_USER + 53 + TB_SETDISABLEDIMAGELIST = WM_USER + 54 + TB_GETDISABLEDIMAGELIST = WM_USER + 55 + TB_SETSTYLE = WM_USER + 56 + TB_GETSTYLE = WM_USER + 57 + TB_SETMAXTEXTROWS = WM_USER + 60 + TB_GETTEXTROWS = WM_USER + 61 + TB_GETOBJECT = WM_USER + 62 + TB_GETBUTTONINFO = WM_USER + 63 + TB_SETBUTTONINFO = WM_USER + 64 + TB_INSERTBUTTON = WM_USER + 67 + TB_ADDBUTTONS = WM_USER + 68 + TB_HITTEST = WM_USER + 69 + TB_SETDRAWTEXTFLAGS = WM_USER + 70 + TB_GETHOTITEM = WM_USER + 71 + TB_SETHOTITEM = WM_USER + 72 + TB_SETANCHORHIGHLIGHT = WM_USER + 73 + TB_GETANCHORHIGHLIGHT = WM_USER + 74 + TB_GETINSERTMARK = WM_USER + 79 + TB_SETINSERTMARK = WM_USER + 80 + TB_INSERTMARKHITTEST = WM_USER + 81 + TB_MOVEBUTTON = WM_USER + 82 + TB_GETMAXSIZE = WM_USER + 83 + TB_SETEXTENDEDSTYLE = WM_USER + 84 + TB_GETEXTENDEDSTYLE = WM_USER + 85 + TB_GETPADDING = WM_USER + 86 + TB_SETPADDING = WM_USER + 87 + TB_SETINSERTMARKCOLOR = WM_USER + 88 + TB_GETINSERTMARKCOLOR = WM_USER + 89 + TB_MAPACCELERATOR = WM_USER + 90 + TB_GETSTRING = WM_USER + 91 + TB_SETCOLORSCHEME = CCM_SETCOLORSCHEME + TB_GETCOLORSCHEME = CCM_GETCOLORSCHEME + TB_SETUNICODEFORMAT = CCM_SETUNICODEFORMAT + TB_GETUNICODEFORMAT = CCM_GETUNICODEFORMAT +) + +// ToolBar notifications +const ( + TBN_FIRST = -700 + TBN_DROPDOWN = TBN_FIRST - 10 +) + +// TBN_DROPDOWN return codes +const ( + TBDDRET_DEFAULT = 0 + TBDDRET_NODEFAULT = 1 + TBDDRET_TREATPRESSED = 2 +) + +// ToolBar state constants +const ( + TBSTATE_CHECKED = 1 + TBSTATE_PRESSED = 2 + TBSTATE_ENABLED = 4 + TBSTATE_HIDDEN = 8 + TBSTATE_INDETERMINATE = 16 + TBSTATE_WRAP = 32 + TBSTATE_ELLIPSES = 0x40 + TBSTATE_MARKED = 0x0080 +) + +// ToolBar style constants +const ( + TBSTYLE_BUTTON = 0 + TBSTYLE_SEP = 1 + TBSTYLE_CHECK = 2 + TBSTYLE_GROUP = 4 + TBSTYLE_CHECKGROUP = TBSTYLE_GROUP | TBSTYLE_CHECK + TBSTYLE_DROPDOWN = 8 + TBSTYLE_AUTOSIZE = 16 + TBSTYLE_NOPREFIX = 32 + TBSTYLE_TOOLTIPS = 256 + TBSTYLE_WRAPABLE = 512 + TBSTYLE_ALTDRAG = 1024 + TBSTYLE_FLAT = 2048 + TBSTYLE_LIST = 4096 + TBSTYLE_CUSTOMERASE = 8192 + TBSTYLE_REGISTERDROP = 0x4000 + TBSTYLE_TRANSPARENT = 0x8000 +) + +// ToolBar extended style constants +const ( + TBSTYLE_EX_DRAWDDARROWS = 0x00000001 + TBSTYLE_EX_MIXEDBUTTONS = 8 + TBSTYLE_EX_HIDECLIPPEDBUTTONS = 16 + TBSTYLE_EX_DOUBLEBUFFER = 0x80 +) + +// ToolBar button style constants +const ( + BTNS_BUTTON = TBSTYLE_BUTTON + BTNS_SEP = TBSTYLE_SEP + BTNS_CHECK = TBSTYLE_CHECK + BTNS_GROUP = TBSTYLE_GROUP + BTNS_CHECKGROUP = TBSTYLE_CHECKGROUP + BTNS_DROPDOWN = TBSTYLE_DROPDOWN + BTNS_AUTOSIZE = TBSTYLE_AUTOSIZE + BTNS_NOPREFIX = TBSTYLE_NOPREFIX + BTNS_WHOLEDROPDOWN = 0x0080 + BTNS_SHOWTEXT = 0x0040 +) + +// TBBUTTONINFO mask flags +const ( + TBIF_IMAGE = 0x00000001 + TBIF_TEXT = 0x00000002 + TBIF_STATE = 0x00000004 + TBIF_STYLE = 0x00000008 + TBIF_LPARAM = 0x00000010 + TBIF_COMMAND = 0x00000020 + TBIF_SIZE = 0x00000040 + TBIF_BYINDEX = 0x80000000 +) + +type NMMOUSE struct { + Hdr NMHDR + DwItemSpec uintptr + DwItemData uintptr + Pt POINT + DwHitInfo uintptr +} + +type NMTOOLBAR struct { + Hdr NMHDR + IItem int32 + TbButton TBBUTTON + CchText int32 + PszText *uint16 + RcButton RECT +} + +type TBBUTTON struct { + IBitmap int32 + IdCommand int32 + FsState byte + FsStyle byte + //#ifdef _WIN64 + // BYTE bReserved[6] // padding for alignment + //#elif defined(_WIN32) + BReserved [2]byte // padding for alignment + //#endif + DwData uintptr + IString uintptr +} + +type TBBUTTONINFO struct { + CbSize uint32 + DwMask uint32 + IdCommand int32 + IImage int32 + FsState byte + FsStyle byte + Cx uint16 + LParam uintptr + PszText uintptr + CchText int32 +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/typedef.go b/v2/internal/frontend/desktop/windows/winc/w32/typedef.go new file mode 100644 index 000000000..c9f38161e --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/typedef.go @@ -0,0 +1,1079 @@ +/* + * Copyright (C) 2019 Tad Vizbaras. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +import ( + "fmt" + "unsafe" +) + +// From MSDN: Windows Data Types +// http://msdn.microsoft.com/en-us/library/s3f49ktz.aspx +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751.aspx +// ATOM WORD +// BOOL int32 +// BOOLEAN byte +// BYTE byte +// CCHAR int8 +// CHAR int8 +// COLORREF DWORD +// DWORD uint32 +// DWORDLONG ULONGLONG +// DWORD_PTR ULONG_PTR +// DWORD32 uint32 +// DWORD64 uint64 +// FLOAT float32 +// HACCEL HANDLE +// HALF_PTR struct{} // ??? +// HANDLE PVOID +// HBITMAP HANDLE +// HBRUSH HANDLE +// HCOLORSPACE HANDLE +// HCONV HANDLE +// HCONVLIST HANDLE +// HCURSOR HANDLE +// HDC HANDLE +// HDDEDATA HANDLE +// HDESK HANDLE +// HDROP HANDLE +// HDWP HANDLE +// HENHMETAFILE HANDLE +// HFILE HANDLE +// HFONT HANDLE +// HGDIOBJ HANDLE +// HGLOBAL HANDLE +// HHOOK HANDLE +// HICON HANDLE +// HINSTANCE HANDLE +// HKEY HANDLE +// HKL HANDLE +// HLOCAL HANDLE +// HMENU HANDLE +// HMETAFILE HANDLE +// HMODULE HANDLE +// HPALETTE HANDLE +// HPEN HANDLE +// HRESULT int32 +// HRGN HANDLE +// HSZ HANDLE +// HWINSTA HANDLE +// HWND HANDLE +// INT int32 +// INT_PTR uintptr +// INT8 int8 +// INT16 int16 +// INT32 int32 +// INT64 int64 +// LANGID WORD +// LCID DWORD +// LCTYPE DWORD +// LGRPID DWORD +// LONG int32 +// LONGLONG int64 +// LONG_PTR uintptr +// LONG32 int32 +// LONG64 int64 +// LPARAM LONG_PTR +// LPBOOL *BOOL +// LPBYTE *BYTE +// LPCOLORREF *COLORREF +// LPCSTR *int8 +// LPCTSTR LPCWSTR +// LPCVOID unsafe.Pointer +// LPCWSTR *WCHAR +// LPDWORD *DWORD +// LPHANDLE *HANDLE +// LPINT *INT +// LPLONG *LONG +// LPSTR *CHAR +// LPTSTR LPWSTR +// LPVOID unsafe.Pointer +// LPWORD *WORD +// LPWSTR *WCHAR +// LRESULT LONG_PTR +// PBOOL *BOOL +// PBOOLEAN *BOOLEAN +// PBYTE *BYTE +// PCHAR *CHAR +// PCSTR *CHAR +// PCTSTR PCWSTR +// PCWSTR *WCHAR +// PDWORD *DWORD +// PDWORDLONG *DWORDLONG +// PDWORD_PTR *DWORD_PTR +// PDWORD32 *DWORD32 +// PDWORD64 *DWORD64 +// PFLOAT *FLOAT +// PHALF_PTR *HALF_PTR +// PHANDLE *HANDLE +// PHKEY *HKEY +// PINT_PTR *INT_PTR +// PINT8 *INT8 +// PINT16 *INT16 +// PINT32 *INT32 +// PINT64 *INT64 +// PLCID *LCID +// PLONG *LONG +// PLONGLONG *LONGLONG +// PLONG_PTR *LONG_PTR +// PLONG32 *LONG32 +// PLONG64 *LONG64 +// POINTER_32 struct{} // ??? +// POINTER_64 struct{} // ??? +// POINTER_SIGNED uintptr +// POINTER_UNSIGNED uintptr +// PSHORT *SHORT +// PSIZE_T *SIZE_T +// PSSIZE_T *SSIZE_T +// PSTR *CHAR +// PTBYTE *TBYTE +// PTCHAR *TCHAR +// PTSTR PWSTR +// PUCHAR *UCHAR +// PUHALF_PTR *UHALF_PTR +// PUINT *UINT +// PUINT_PTR *UINT_PTR +// PUINT8 *UINT8 +// PUINT16 *UINT16 +// PUINT32 *UINT32 +// PUINT64 *UINT64 +// PULONG *ULONG +// PULONGLONG *ULONGLONG +// PULONG_PTR *ULONG_PTR +// PULONG32 *ULONG32 +// PULONG64 *ULONG64 +// PUSHORT *USHORT +// PVOID unsafe.Pointer +// PWCHAR *WCHAR +// PWORD *WORD +// PWSTR *WCHAR +// QWORD uint64 +// SC_HANDLE HANDLE +// SC_LOCK LPVOID +// SERVICE_STATUS_HANDLE HANDLE +// SHORT int16 +// SIZE_T ULONG_PTR +// SSIZE_T LONG_PTR +// TBYTE WCHAR +// TCHAR WCHAR +// UCHAR uint8 +// UHALF_PTR struct{} // ??? +// UINT uint32 +// UINT_PTR uintptr +// UINT8 uint8 +// UINT16 uint16 +// UINT32 uint32 +// UINT64 uint64 +// ULONG uint32 +// ULONGLONG uint64 +// ULONG_PTR uintptr +// ULONG32 uint32 +// ULONG64 uint64 +// USHORT uint16 +// USN LONGLONG +// WCHAR uint16 +// WORD uint16 +// WPARAM UINT_PTR +type ( + ATOM = uint16 + BOOL = int32 + COLORREF = uint32 + DWM_FRAME_COUNT = uint64 + WORD = uint16 + DWORD = uint32 + HACCEL = HANDLE + HANDLE = uintptr + HBITMAP = HANDLE + HBRUSH = HANDLE + HCURSOR = HANDLE + HDC = HANDLE + HDROP = HANDLE + HDWP = HANDLE + HENHMETAFILE = HANDLE + HFONT = HANDLE + HGDIOBJ = HANDLE + HGLOBAL = HANDLE + HGLRC = HANDLE + HHOOK = HANDLE + HICON = HANDLE + HIMAGELIST = HANDLE + HINSTANCE = HANDLE + HKEY = HANDLE + HKL = HANDLE + HMENU = HANDLE + HMODULE = HANDLE + HMONITOR = HANDLE + HPEN = HANDLE + HRESULT = int32 + HRGN = HANDLE + HRSRC = HANDLE + HTHUMBNAIL = HANDLE + HWND = HANDLE + LPARAM = uintptr + LPCVOID = unsafe.Pointer + LRESULT = uintptr + PVOID = unsafe.Pointer + QPC_TIME = uint64 + ULONG_PTR = uintptr + SIZE_T = ULONG_PTR + WPARAM = uintptr + UINT = uint +) + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd162805.aspx +type POINT struct { + X, Y int32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd162897.aspx +type RECT struct { + Left, Top, Right, Bottom int32 +} + +func (r *RECT) String() string { + return fmt.Sprintf("RECT (%p): Left: %d, Top: %d, Right: %d, Bottom: %d", r, r.Left, r.Top, r.Right, r.Bottom) +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms633577.aspx +type WNDCLASSEX struct { + Size uint32 + Style uint32 + WndProc uintptr + ClsExtra int32 + WndExtra int32 + Instance HINSTANCE + Icon HICON + Cursor HCURSOR + Background HBRUSH + MenuName *uint16 + ClassName *uint16 + IconSm HICON +} + +type TPMPARAMS struct { + CbSize uint32 + RcExclude RECT +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms644958.aspx +type MSG struct { + Hwnd HWND + Message uint32 + WParam uintptr + LParam uintptr + Time uint32 + Pt POINT +} + +// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-minmaxinfo +type MINMAXINFO struct { + PtReserved POINT + PtMaxSize POINT + PtMaxPosition POINT + PtMinTrackSize POINT + PtMaxTrackSize POINT +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd145037.aspx +type LOGFONT struct { + Height int32 + Width int32 + Escapement int32 + Orientation int32 + Weight int32 + Italic byte + Underline byte + StrikeOut byte + CharSet byte + OutPrecision byte + ClipPrecision byte + Quality byte + PitchAndFamily byte + FaceName [LF_FACESIZE]uint16 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646839.aspx +type OPENFILENAME struct { + StructSize uint32 + Owner HWND + Instance HINSTANCE + Filter *uint16 + CustomFilter *uint16 + MaxCustomFilter uint32 + FilterIndex uint32 + File *uint16 + MaxFile uint32 + FileTitle *uint16 + MaxFileTitle uint32 + InitialDir *uint16 + Title *uint16 + Flags uint32 + FileOffset uint16 + FileExtension uint16 + DefExt *uint16 + CustData uintptr + FnHook uintptr + TemplateName *uint16 + PvReserved unsafe.Pointer + DwReserved uint32 + FlagsEx uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb773205.aspx +type BROWSEINFO struct { + Owner HWND + Root *uint16 + DisplayName *uint16 + Title *uint16 + Flags uint32 + CallbackFunc uintptr + LParam uintptr + Image int32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373931.aspx +type GUID struct { + Data1 uint32 + Data2 uint16 + Data3 uint16 + Data4 [8]byte +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms221627.aspx +type VARIANT struct { + VT uint16 // 2 + WReserved1 uint16 // 4 + WReserved2 uint16 // 6 + WReserved3 uint16 // 8 + Val int64 // 16 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms221416.aspx +type DISPPARAMS struct { + Rgvarg uintptr + RgdispidNamedArgs uintptr + CArgs uint32 + CNamedArgs uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms221133.aspx +type EXCEPINFO struct { + WCode uint16 + WReserved uint16 + BstrSource *uint16 + BstrDescription *uint16 + BstrHelpFile *uint16 + DwHelpContext uint32 + PvReserved uintptr + PfnDeferredFillIn uintptr + Scode int32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd145035.aspx +type LOGBRUSH struct { + LbStyle uint32 + LbColor COLORREF + LbHatch uintptr +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd183565.aspx +type DEVMODE struct { + DmDeviceName [CCHDEVICENAME]uint16 + DmSpecVersion uint16 + DmDriverVersion uint16 + DmSize uint16 + DmDriverExtra uint16 + DmFields uint32 + DmOrientation int16 + DmPaperSize int16 + DmPaperLength int16 + DmPaperWidth int16 + DmScale int16 + DmCopies int16 + DmDefaultSource int16 + DmPrintQuality int16 + DmColor int16 + DmDuplex int16 + DmYResolution int16 + DmTTOption int16 + DmCollate int16 + DmFormName [CCHFORMNAME]uint16 + DmLogPixels uint16 + DmBitsPerPel uint32 + DmPelsWidth uint32 + DmPelsHeight uint32 + DmDisplayFlags uint32 + DmDisplayFrequency uint32 + DmICMMethod uint32 + DmICMIntent uint32 + DmMediaType uint32 + DmDitherType uint32 + DmReserved1 uint32 + DmReserved2 uint32 + DmPanningWidth uint32 + DmPanningHeight uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd183376.aspx +type BITMAPINFOHEADER struct { + BiSize uint32 + BiWidth int32 + BiHeight int32 + BiPlanes uint16 + BiBitCount uint16 + BiCompression uint32 + BiSizeImage uint32 + BiXPelsPerMeter int32 + BiYPelsPerMeter int32 + BiClrUsed uint32 + BiClrImportant uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd162938.aspx +type RGBQUAD struct { + RgbBlue byte + RgbGreen byte + RgbRed byte + RgbReserved byte +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd183375.aspx +type BITMAPINFO struct { + BmiHeader BITMAPINFOHEADER + BmiColors *RGBQUAD +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd183371.aspx +type BITMAP struct { + BmType int32 + BmWidth int32 + BmHeight int32 + BmWidthBytes int32 + BmPlanes uint16 + BmBitsPixel uint16 + BmBits unsafe.Pointer +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd183567.aspx +type DIBSECTION struct { + DsBm BITMAP + DsBmih BITMAPINFOHEADER + DsBitfields [3]uint32 + DshSection HANDLE + DsOffset uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd162607.aspx +type ENHMETAHEADER struct { + IType uint32 + NSize uint32 + RclBounds RECT + RclFrame RECT + DSignature uint32 + NVersion uint32 + NBytes uint32 + NRecords uint32 + NHandles uint16 + SReserved uint16 + NDescription uint32 + OffDescription uint32 + NPalEntries uint32 + SzlDevice SIZE + SzlMillimeters SIZE + CbPixelFormat uint32 + OffPixelFormat uint32 + BOpenGL uint32 + SzlMicrometers SIZE +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd145106.aspx +type SIZE struct { + CX, CY int32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd145132.aspx +type TEXTMETRIC struct { + TmHeight int32 + TmAscent int32 + TmDescent int32 + TmInternalLeading int32 + TmExternalLeading int32 + TmAveCharWidth int32 + TmMaxCharWidth int32 + TmWeight int32 + TmOverhang int32 + TmDigitizedAspectX int32 + TmDigitizedAspectY int32 + TmFirstChar uint16 + TmLastChar uint16 + TmDefaultChar uint16 + TmBreakChar uint16 + TmItalic byte + TmUnderlined byte + TmStruckOut byte + TmPitchAndFamily byte + TmCharSet byte +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd183574.aspx +type DOCINFO struct { + CbSize int32 + LpszDocName *uint16 + LpszOutput *uint16 + LpszDatatype *uint16 + FwType uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb775514.aspx +type NMHDR struct { + HwndFrom HWND + IdFrom uintptr + Code uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb774743.aspx +type LVCOLUMN struct { + Mask uint32 + Fmt int32 + Cx int32 + PszText *uint16 + CchTextMax int32 + ISubItem int32 + IImage int32 + IOrder int32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb774760.aspx +type LVITEM struct { + Mask uint32 + IItem int32 + ISubItem int32 + State uint32 + StateMask uint32 + PszText *uint16 + CchTextMax int32 + IImage int32 + LParam uintptr + IIndent int32 + IGroupId int32 + CColumns uint32 + PuColumns uint32 +} + +type LVFINDINFO struct { + Flags uint32 + PszText *uint16 + LParam uintptr + Pt POINT + VkDirection uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb774754.aspx +type LVHITTESTINFO struct { + Pt POINT + Flags uint32 + IItem int32 + ISubItem int32 + IGroup int32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb774771.aspx +type NMITEMACTIVATE struct { + Hdr NMHDR + IItem int32 + ISubItem int32 + UNewState uint32 + UOldState uint32 + UChanged uint32 + PtAction POINT + LParam uintptr + UKeyFlags uint32 +} + +type NMLVKEYDOWN struct { + Hdr NMHDR + WVKey uint16 + Flags uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb774773.aspx +type NMLISTVIEW struct { + Hdr NMHDR + IItem int32 + ISubItem int32 + UNewState uint32 + UOldState uint32 + UChanged uint32 + PtAction POINT + LParam uintptr +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb774780.aspx +type NMLVDISPINFO struct { + Hdr NMHDR + Item LVITEM +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb775507.aspx +type INITCOMMONCONTROLSEX struct { + DwSize uint32 + DwICC uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb760256.aspx +type TOOLINFO struct { + CbSize uint32 + UFlags uint32 + Hwnd HWND + UId uintptr + Rect RECT + Hinst HINSTANCE + LpszText *uint16 + LParam uintptr + LpReserved unsafe.Pointer +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms645604.aspx +type TRACKMOUSEEVENT struct { + CbSize uint32 + DwFlags uint32 + HwndTrack HWND + DwHoverTime uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms534067.aspx +type GdiplusStartupInput struct { + GdiplusVersion uint32 + DebugEventCallback uintptr + SuppressBackgroundThread BOOL + SuppressExternalCodecs BOOL +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms534068.aspx +type GdiplusStartupOutput struct { + NotificationHook uintptr + NotificationUnhook uintptr +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd162768.aspx +type PAINTSTRUCT struct { + Hdc HDC + FErase BOOL + RcPaint RECT + FRestore BOOL + FIncUpdate BOOL + RgbReserved [32]byte +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa363646.aspx +type EVENTLOGRECORD struct { + Length uint32 + Reserved uint32 + RecordNumber uint32 + TimeGenerated uint32 + TimeWritten uint32 + EventID uint32 + EventType uint16 + NumStrings uint16 + EventCategory uint16 + ReservedFlags uint16 + ClosingRecordNumber uint32 + StringOffset uint32 + UserSidLength uint32 + UserSidOffset uint32 + DataLength uint32 + DataOffset uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms685996.aspx +type SERVICE_STATUS struct { + DwServiceType uint32 + DwCurrentState uint32 + DwControlsAccepted uint32 + DwWin32ExitCode uint32 + DwServiceSpecificExitCode uint32 + DwCheckPoint uint32 + DwWaitHint uint32 +} + +/* ------------------------- + Undocumented API +------------------------- */ + +type ACCENT_STATE DWORD + +const ( + ACCENT_DISABLED ACCENT_STATE = 0 + ACCENT_ENABLE_GRADIENT ACCENT_STATE = 1 + ACCENT_ENABLE_TRANSPARENTGRADIENT ACCENT_STATE = 2 + ACCENT_ENABLE_BLURBEHIND ACCENT_STATE = 3 + ACCENT_ENABLE_ACRYLICBLURBEHIND ACCENT_STATE = 4 // RS4 1803 + ACCENT_ENABLE_HOSTBACKDROP ACCENT_STATE = 5 // RS5 1809 + ACCENT_INVALID_STATE ACCENT_STATE = 6 +) + +type ACCENT_POLICY struct { + AccentState ACCENT_STATE + AccentFlags DWORD + GradientColor DWORD + AnimationId DWORD +} + +type WINDOWCOMPOSITIONATTRIBDATA struct { + Attrib WINDOWCOMPOSITIONATTRIB + PvData PVOID + CbData SIZE_T +} + +type WINDOWCOMPOSITIONATTRIB DWORD + +const ( + WCA_UNDEFINED WINDOWCOMPOSITIONATTRIB = 0 + WCA_NCRENDERING_ENABLED WINDOWCOMPOSITIONATTRIB = 1 + WCA_NCRENDERING_POLICY WINDOWCOMPOSITIONATTRIB = 2 + WCA_TRANSITIONS_FORCEDISABLED WINDOWCOMPOSITIONATTRIB = 3 + WCA_ALLOW_NCPAINT WINDOWCOMPOSITIONATTRIB = 4 + WCA_CAPTION_BUTTON_BOUNDS WINDOWCOMPOSITIONATTRIB = 5 + WCA_NONCLIENT_RTL_LAYOUT WINDOWCOMPOSITIONATTRIB = 6 + WCA_FORCE_ICONIC_REPRESENTATION WINDOWCOMPOSITIONATTRIB = 7 + WCA_EXTENDED_FRAME_BOUNDS WINDOWCOMPOSITIONATTRIB = 8 + WCA_HAS_ICONIC_BITMAP WINDOWCOMPOSITIONATTRIB = 9 + WCA_THEME_ATTRIBUTES WINDOWCOMPOSITIONATTRIB = 10 + WCA_NCRENDERING_EXILED WINDOWCOMPOSITIONATTRIB = 11 + WCA_NCADORNMENTINFO WINDOWCOMPOSITIONATTRIB = 12 + WCA_EXCLUDED_FROM_LIVEPREVIEW WINDOWCOMPOSITIONATTRIB = 13 + WCA_VIDEO_OVERLAY_ACTIVE WINDOWCOMPOSITIONATTRIB = 14 + WCA_FORCE_ACTIVEWINDOW_APPEARANCE WINDOWCOMPOSITIONATTRIB = 15 + WCA_DISALLOW_PEEK WINDOWCOMPOSITIONATTRIB = 16 + WCA_CLOAK WINDOWCOMPOSITIONATTRIB = 17 + WCA_CLOAKED WINDOWCOMPOSITIONATTRIB = 18 + WCA_ACCENT_POLICY WINDOWCOMPOSITIONATTRIB = 19 + WCA_FREEZE_REPRESENTATION WINDOWCOMPOSITIONATTRIB = 20 + WCA_EVER_UNCLOAKED WINDOWCOMPOSITIONATTRIB = 21 + WCA_VISUAL_OWNER WINDOWCOMPOSITIONATTRIB = 22 + WCA_HOLOGRAPHIC WINDOWCOMPOSITIONATTRIB = 23 + WCA_EXCLUDED_FROM_DDA WINDOWCOMPOSITIONATTRIB = 24 + WCA_PASSIVEUPDATEMODE WINDOWCOMPOSITIONATTRIB = 25 + WCA_USEDARKMODECOLORS WINDOWCOMPOSITIONATTRIB = 26 + WCA_CORNER_STYLE WINDOWCOMPOSITIONATTRIB = 27 + WCA_PART_COLOR WINDOWCOMPOSITIONATTRIB = 28 + WCA_DISABLE_MOVESIZE_FEEDBACK WINDOWCOMPOSITIONATTRIB = 29 + WCA_LAST WINDOWCOMPOSITIONATTRIB = 30 +) + +// ------------------------- + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms684225.aspx +type MODULEENTRY32 struct { + Size uint32 + ModuleID uint32 + ProcessID uint32 + GlblcntUsage uint32 + ProccntUsage uint32 + ModBaseAddr *uint8 + ModBaseSize uint32 + HModule HMODULE + SzModule [MAX_MODULE_NAME32 + 1]uint16 + SzExePath [MAX_PATH]uint16 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724284.aspx +type FILETIME struct { + DwLowDateTime uint32 + DwHighDateTime uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682119.aspx +type COORD struct { + X, Y int16 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms686311.aspx +type SMALL_RECT struct { + Left, Top, Right, Bottom int16 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093.aspx +type CONSOLE_SCREEN_BUFFER_INFO struct { + DwSize COORD + DwCursorPosition COORD + WAttributes uint16 + SrWindow SMALL_RECT + DwMaximumWindowSize COORD +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/bb773244.aspx +type MARGINS struct { + CxLeftWidth, CxRightWidth, CyTopHeight, CyBottomHeight int32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa969500.aspx +type DWM_BLURBEHIND struct { + DwFlags uint32 + fEnable BOOL + hRgnBlur HRGN + fTransitionOnMaximized BOOL +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa969501.aspx +type DWM_PRESENT_PARAMETERS struct { + cbSize uint32 + fQueue BOOL + cRefreshStart DWM_FRAME_COUNT + cBuffer uint32 + fUseSourceRate BOOL + rateSource UNSIGNED_RATIO + cRefreshesPerFrame uint32 + eSampling DWM_SOURCE_FRAME_SAMPLING +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa969502.aspx +type DWM_THUMBNAIL_PROPERTIES struct { + dwFlags uint32 + rcDestination RECT + rcSource RECT + opacity byte + fVisible BOOL + fSourceClientAreaOnly BOOL +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa969503.aspx +type DWM_TIMING_INFO struct { + cbSize uint32 + rateRefresh UNSIGNED_RATIO + qpcRefreshPeriod QPC_TIME + rateCompose UNSIGNED_RATIO + qpcVBlank QPC_TIME + cRefresh DWM_FRAME_COUNT + cDXRefresh uint32 + qpcCompose QPC_TIME + cFrame DWM_FRAME_COUNT + cDXPresent uint32 + cRefreshFrame DWM_FRAME_COUNT + cFrameSubmitted DWM_FRAME_COUNT + cDXPresentSubmitted uint32 + cFrameConfirmed DWM_FRAME_COUNT + cDXPresentConfirmed uint32 + cRefreshConfirmed DWM_FRAME_COUNT + cDXRefreshConfirmed uint32 + cFramesLate DWM_FRAME_COUNT + cFramesOutstanding uint32 + cFrameDisplayed DWM_FRAME_COUNT + qpcFrameDisplayed QPC_TIME + cRefreshFrameDisplayed DWM_FRAME_COUNT + cFrameComplete DWM_FRAME_COUNT + qpcFrameComplete QPC_TIME + cFramePending DWM_FRAME_COUNT + qpcFramePending QPC_TIME + cFramesDisplayed DWM_FRAME_COUNT + cFramesComplete DWM_FRAME_COUNT + cFramesPending DWM_FRAME_COUNT + cFramesAvailable DWM_FRAME_COUNT + cFramesDropped DWM_FRAME_COUNT + cFramesMissed DWM_FRAME_COUNT + cRefreshNextDisplayed DWM_FRAME_COUNT + cRefreshNextPresented DWM_FRAME_COUNT + cRefreshesDisplayed DWM_FRAME_COUNT + cRefreshesPresented DWM_FRAME_COUNT + cRefreshStarted DWM_FRAME_COUNT + cPixelsReceived uint64 + cPixelsDrawn uint64 + cBuffersEmpty DWM_FRAME_COUNT +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd389402.aspx +type MilMatrix3x2D struct { + S_11, S_12, S_21, S_22 float64 + DX, DY float64 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa969505.aspx +type UNSIGNED_RATIO struct { + uiNumerator uint32 + uiDenominator uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms632603.aspx +type CREATESTRUCT struct { + CreateParams uintptr + Instance HINSTANCE + Menu HMENU + Parent HWND + Cy, Cx int32 + Y, X int32 + Style int32 + Name *uint16 + Class *uint16 + dwExStyle uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd145065.aspx +type MONITORINFO struct { + CbSize uint32 + RcMonitor RECT + RcWork RECT + DwFlags uint32 +} + +type WINDOWINFO struct { + CbSize DWORD + RcWindow RECT + RcClient RECT + DwStyle DWORD + DwExStyle DWORD + DwWindowStatus DWORD + CxWindowBorders UINT + CyWindowBorders UINT + AtomWindowType ATOM + WCreatorVersion WORD +} + +type MONITOR_DPI_TYPE int32 + +const ( + MDT_EFFECTIVE_DPI MONITOR_DPI_TYPE = 0 + MDT_ANGULAR_DPI MONITOR_DPI_TYPE = 1 + MDT_RAW_DPI MONITOR_DPI_TYPE = 2 + MDT_DEFAULT MONITOR_DPI_TYPE = 0 +) + +func (w *WINDOWINFO) isStyle(style DWORD) bool { + return w.DwStyle&style == style +} + +func (w *WINDOWINFO) IsPopup() bool { + return w.isStyle(WS_POPUP) +} + +func (m *MONITORINFO) Dump() { + fmt.Printf("MONITORINFO (%p)\n", m) + fmt.Printf(" CbSize : %d\n", m.CbSize) + fmt.Printf(" RcMonitor: %s\n", &m.RcMonitor) + fmt.Printf(" RcWork : %s\n", &m.RcWork) + fmt.Printf(" DwFlags : %d\n", m.DwFlags) +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd145066.aspx +type MONITORINFOEX struct { + MONITORINFO + SzDevice [CCHDEVICENAME]uint16 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd368826.aspx +type PIXELFORMATDESCRIPTOR struct { + Size uint16 + Version uint16 + DwFlags uint32 + IPixelType byte + ColorBits byte + RedBits, RedShift byte + GreenBits, GreenShift byte + BlueBits, BlueShift byte + AlphaBits, AlphaShift byte + AccumBits byte + AccumRedBits byte + AccumGreenBits byte + AccumBlueBits byte + AccumAlphaBits byte + DepthBits, StencilBits byte + AuxBuffers byte + ILayerType byte + Reserved byte + DwLayerMask uint32 + DwVisibleMask uint32 + DwDamageMask uint32 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646270(v=vs.85).aspx +type INPUT struct { + Type uint32 + Mi MOUSEINPUT + Ki KEYBDINPUT + Hi HARDWAREINPUT +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646273(v=vs.85).aspx +type MOUSEINPUT struct { + Dx int32 + Dy int32 + MouseData uint32 + DwFlags uint32 + Time uint32 + DwExtraInfo uintptr +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646271(v=vs.85).aspx +type KEYBDINPUT struct { + WVk uint16 + WScan uint16 + DwFlags uint32 + Time uint32 + DwExtraInfo uintptr +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646269(v=vs.85).aspx +type HARDWAREINPUT struct { + UMsg uint32 + WParamL uint16 + WParamH uint16 +} + +type KbdInput struct { + typ uint32 + ki KEYBDINPUT +} + +type MouseInput struct { + typ uint32 + mi MOUSEINPUT +} + +type HardwareInput struct { + typ uint32 + hi HARDWAREINPUT +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx +type SYSTEMTIME struct { + Year uint16 + Month uint16 + DayOfWeek uint16 + Day uint16 + Hour uint16 + Minute uint16 + Second uint16 + Milliseconds uint16 +} + +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms644967(v=vs.85).aspx +type KBDLLHOOKSTRUCT struct { + VkCode DWORD + ScanCode DWORD + Flags DWORD + Time DWORD + DwExtraInfo ULONG_PTR +} + +type HOOKPROC func(int, WPARAM, LPARAM) LRESULT + +type WINDOWPLACEMENT struct { + Length uint32 + Flags uint32 + ShowCmd uint32 + PtMinPosition POINT + PtMaxPosition POINT + RcNormalPosition RECT +} + +type SCROLLINFO struct { + CbSize uint32 + FMask uint32 + NMin int32 + NMax int32 + NPage uint32 + NPos int32 + NTrackPos int32 +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/user32.go b/v2/internal/frontend/desktop/windows/winc/w32/user32.go new file mode 100644 index 000000000..a6a744d19 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/user32.go @@ -0,0 +1,1260 @@ +/* + * Copyright (C) 2019 Tad Vizbaras. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +import ( + "fmt" + "runtime" + "syscall" + "unsafe" +) + +var ( + moduser32 = syscall.NewLazyDLL("user32.dll") + + procRegisterClassEx = moduser32.NewProc("RegisterClassExW") + procLoadIcon = moduser32.NewProc("LoadIconW") + procLoadCursor = moduser32.NewProc("LoadCursorW") + procShowWindow = moduser32.NewProc("ShowWindow") + procShowWindowAsync = moduser32.NewProc("ShowWindowAsync") + procUpdateWindow = moduser32.NewProc("UpdateWindow") + procCreateWindowEx = moduser32.NewProc("CreateWindowExW") + procAdjustWindowRect = moduser32.NewProc("AdjustWindowRect") + procAdjustWindowRectEx = moduser32.NewProc("AdjustWindowRectEx") + procDestroyWindow = moduser32.NewProc("DestroyWindow") + procDefWindowProc = moduser32.NewProc("DefWindowProcW") + procDefDlgProc = moduser32.NewProc("DefDlgProcW") + procPostQuitMessage = moduser32.NewProc("PostQuitMessage") + procGetMessage = moduser32.NewProc("GetMessageW") + procTranslateMessage = moduser32.NewProc("TranslateMessage") + procDispatchMessage = moduser32.NewProc("DispatchMessageW") + procSendMessage = moduser32.NewProc("SendMessageW") + procPostMessage = moduser32.NewProc("PostMessageW") + procWaitMessage = moduser32.NewProc("WaitMessage") + procSetWindowText = moduser32.NewProc("SetWindowTextW") + procGetWindowTextLength = moduser32.NewProc("GetWindowTextLengthW") + procGetWindowText = moduser32.NewProc("GetWindowTextW") + procGetWindowRect = moduser32.NewProc("GetWindowRect") + procGetWindowInfo = moduser32.NewProc("GetWindowInfo") + procSetWindowCompositionAttribute = moduser32.NewProc("SetWindowCompositionAttribute") + procMoveWindow = moduser32.NewProc("MoveWindow") + procScreenToClient = moduser32.NewProc("ScreenToClient") + procCallWindowProc = moduser32.NewProc("CallWindowProcW") + procSetWindowLong = moduser32.NewProc("SetWindowLongW") + procSetWindowLongPtr = moduser32.NewProc("SetWindowLongW") + procGetWindowLong = moduser32.NewProc("GetWindowLongW") + procGetWindowLongPtr = moduser32.NewProc("GetWindowLongW") + procEnableWindow = moduser32.NewProc("EnableWindow") + procIsWindowEnabled = moduser32.NewProc("IsWindowEnabled") + procIsWindowVisible = moduser32.NewProc("IsWindowVisible") + procSetFocus = moduser32.NewProc("SetFocus") + procGetFocus = moduser32.NewProc("GetFocus") + procSetActiveWindow = moduser32.NewProc("SetActiveWindow") + procSetForegroundWindow = moduser32.NewProc("SetForegroundWindow") + procBringWindowToTop = moduser32.NewProc("BringWindowToTop") + procInvalidateRect = moduser32.NewProc("InvalidateRect") + procGetClientRect = moduser32.NewProc("GetClientRect") + procGetDC = moduser32.NewProc("GetDC") + procReleaseDC = moduser32.NewProc("ReleaseDC") + procSetCapture = moduser32.NewProc("SetCapture") + procReleaseCapture = moduser32.NewProc("ReleaseCapture") + procGetWindowThreadProcessId = moduser32.NewProc("GetWindowThreadProcessId") + procMessageBox = moduser32.NewProc("MessageBoxW") + procGetSystemMetrics = moduser32.NewProc("GetSystemMetrics") + procPostThreadMessageW = moduser32.NewProc("PostThreadMessageW") + procRegisterWindowMessageA = moduser32.NewProc("RegisterWindowMessageA") + //procSysColorBrush = moduser32.NewProc("GetSysColorBrush") + procCopyRect = moduser32.NewProc("CopyRect") + procEqualRect = moduser32.NewProc("EqualRect") + procInflateRect = moduser32.NewProc("InflateRect") + procIntersectRect = moduser32.NewProc("IntersectRect") + procIsRectEmpty = moduser32.NewProc("IsRectEmpty") + procOffsetRect = moduser32.NewProc("OffsetRect") + procPtInRect = moduser32.NewProc("PtInRect") + procSetRect = moduser32.NewProc("SetRect") + procSetRectEmpty = moduser32.NewProc("SetRectEmpty") + procSubtractRect = moduser32.NewProc("SubtractRect") + procUnionRect = moduser32.NewProc("UnionRect") + procCreateDialogParam = moduser32.NewProc("CreateDialogParamW") + procDialogBoxParam = moduser32.NewProc("DialogBoxParamW") + procGetDlgItem = moduser32.NewProc("GetDlgItem") + procDrawIcon = moduser32.NewProc("DrawIcon") + procCreateMenu = moduser32.NewProc("CreateMenu") + //procSetMenu = moduser32.NewProc("SetMenu") + procDestroyMenu = moduser32.NewProc("DestroyMenu") + procCreatePopupMenu = moduser32.NewProc("CreatePopupMenu") + procCheckMenuRadioItem = moduser32.NewProc("CheckMenuRadioItem") + //procDrawMenuBar = moduser32.NewProc("DrawMenuBar") + //procInsertMenuItem = moduser32.NewProc("InsertMenuItemW") // FIXIT: + + procClientToScreen = moduser32.NewProc("ClientToScreen") + procIsDialogMessage = moduser32.NewProc("IsDialogMessageW") + procIsWindow = moduser32.NewProc("IsWindow") + procEndDialog = moduser32.NewProc("EndDialog") + procPeekMessage = moduser32.NewProc("PeekMessageW") + procTranslateAccelerator = moduser32.NewProc("TranslateAcceleratorW") + procSetWindowPos = moduser32.NewProc("SetWindowPos") + procFillRect = moduser32.NewProc("FillRect") + procDrawText = moduser32.NewProc("DrawTextW") + procAddClipboardFormatListener = moduser32.NewProc("AddClipboardFormatListener") + procRemoveClipboardFormatListener = moduser32.NewProc("RemoveClipboardFormatListener") + procOpenClipboard = moduser32.NewProc("OpenClipboard") + procCloseClipboard = moduser32.NewProc("CloseClipboard") + procEnumClipboardFormats = moduser32.NewProc("EnumClipboardFormats") + procGetClipboardData = moduser32.NewProc("GetClipboardData") + procSetClipboardData = moduser32.NewProc("SetClipboardData") + procEmptyClipboard = moduser32.NewProc("EmptyClipboard") + procGetClipboardFormatName = moduser32.NewProc("GetClipboardFormatNameW") + procIsClipboardFormatAvailable = moduser32.NewProc("IsClipboardFormatAvailable") + procBeginPaint = moduser32.NewProc("BeginPaint") + procEndPaint = moduser32.NewProc("EndPaint") + procGetKeyboardState = moduser32.NewProc("GetKeyboardState") + procMapVirtualKey = moduser32.NewProc("MapVirtualKeyExW") + procGetAsyncKeyState = moduser32.NewProc("GetAsyncKeyState") + procToAscii = moduser32.NewProc("ToAscii") + procSwapMouseButton = moduser32.NewProc("SwapMouseButton") + procGetCursorPos = moduser32.NewProc("GetCursorPos") + procSetCursorPos = moduser32.NewProc("SetCursorPos") + procSetCursor = moduser32.NewProc("SetCursor") + procCreateIcon = moduser32.NewProc("CreateIcon") + procDestroyIcon = moduser32.NewProc("DestroyIcon") + procMonitorFromPoint = moduser32.NewProc("MonitorFromPoint") + procMonitorFromRect = moduser32.NewProc("MonitorFromRect") + procMonitorFromWindow = moduser32.NewProc("MonitorFromWindow") + procGetMonitorInfo = moduser32.NewProc("GetMonitorInfoW") + procGetDpiForSystem = moduser32.NewProc("GetDpiForSystem") + procGetDpiForWindow = moduser32.NewProc("GetDpiForWindow") + procEnumDisplayMonitors = moduser32.NewProc("EnumDisplayMonitors") + procEnumDisplaySettingsEx = moduser32.NewProc("EnumDisplaySettingsExW") + procChangeDisplaySettingsEx = moduser32.NewProc("ChangeDisplaySettingsExW") + procSendInput = moduser32.NewProc("SendInput") + procSetWindowsHookEx = moduser32.NewProc("SetWindowsHookExW") + procUnhookWindowsHookEx = moduser32.NewProc("UnhookWindowsHookEx") + procCallNextHookEx = moduser32.NewProc("CallNextHookEx") + + libuser32, _ = syscall.LoadLibrary("user32.dll") + insertMenuItem, _ = syscall.GetProcAddress(libuser32, "InsertMenuItemW") + setMenuItemInfo, _ = syscall.GetProcAddress(libuser32, "SetMenuItemInfoW") + setMenu, _ = syscall.GetProcAddress(libuser32, "SetMenu") + drawMenuBar, _ = syscall.GetProcAddress(libuser32, "DrawMenuBar") + trackPopupMenuEx, _ = syscall.GetProcAddress(libuser32, "TrackPopupMenuEx") + getKeyState, _ = syscall.GetProcAddress(libuser32, "GetKeyState") + getSysColorBrush, _ = syscall.GetProcAddress(libuser32, "GetSysColorBrush") + + getWindowPlacement, _ = syscall.GetProcAddress(libuser32, "GetWindowPlacement") + setWindowPlacement, _ = syscall.GetProcAddress(libuser32, "SetWindowPlacement") + + setScrollInfo, _ = syscall.GetProcAddress(libuser32, "SetScrollInfo") + getScrollInfo, _ = syscall.GetProcAddress(libuser32, "GetScrollInfo") + + mainThread HANDLE +) + +func init() { + runtime.LockOSThread() + mainThread = GetCurrentThreadId() +} + +func GET_X_LPARAM(lp uintptr) int32 { + return int32(int16(LOWORD(uint32(lp)))) +} + +func GET_Y_LPARAM(lp uintptr) int32 { + return int32(int16(HIWORD(uint32(lp)))) +} + +func RegisterClassEx(wndClassEx *WNDCLASSEX) ATOM { + ret, _, _ := procRegisterClassEx.Call(uintptr(unsafe.Pointer(wndClassEx))) + return ATOM(ret) +} + +func LoadIcon(instance HINSTANCE, iconName *uint16) HICON { + ret, _, _ := procLoadIcon.Call( + uintptr(instance), + uintptr(unsafe.Pointer(iconName))) + + return HICON(ret) +} + +func LoadCursor(instance HINSTANCE, cursorName *uint16) HCURSOR { + ret, _, _ := procLoadCursor.Call( + uintptr(instance), + uintptr(unsafe.Pointer(cursorName))) + + return HCURSOR(ret) + +} + +func ShowWindow(hwnd HWND, cmdshow int) bool { + ret, _, _ := procShowWindow.Call( + uintptr(hwnd), + uintptr(cmdshow)) + + return ret != 0 +} + +func ShowWindowAsync(hwnd HWND, cmdshow int) bool { + ret, _, _ := procShowWindowAsync.Call( + uintptr(hwnd), + uintptr(cmdshow)) + + return ret != 0 +} + +func UpdateWindow(hwnd HWND) bool { + ret, _, _ := procUpdateWindow.Call( + uintptr(hwnd)) + return ret != 0 +} + +func PostThreadMessage(threadID HANDLE, msg int, wp, lp uintptr) { + procPostThreadMessageW.Call(threadID, uintptr(msg), wp, lp) +} + +func RegisterWindowMessage(name *uint16) uint32 { + ret, _, _ := procRegisterWindowMessageA.Call( + uintptr(unsafe.Pointer(name))) + + return uint32(ret) +} + +func PostMainThreadMessage(msg uint32, wp, lp uintptr) bool { + ret, _, _ := procPostThreadMessageW.Call(mainThread, uintptr(msg), wp, lp) + return ret != 0 +} + +func CreateWindowEx(exStyle uint, className, windowName *uint16, + style uint, x, y, width, height int, parent HWND, menu HMENU, + instance HINSTANCE, param unsafe.Pointer) HWND { + ret, _, _ := procCreateWindowEx.Call( + uintptr(exStyle), + uintptr(unsafe.Pointer(className)), + uintptr(unsafe.Pointer(windowName)), + uintptr(style), + uintptr(x), + uintptr(y), + uintptr(width), + uintptr(height), + uintptr(parent), + uintptr(menu), + uintptr(instance), + uintptr(param)) + + return HWND(ret) +} + +func AdjustWindowRectEx(rect *RECT, style uint, menu bool, exStyle uint) bool { + ret, _, _ := procAdjustWindowRectEx.Call( + uintptr(unsafe.Pointer(rect)), + uintptr(style), + uintptr(BoolToBOOL(menu)), + uintptr(exStyle)) + + return ret != 0 +} + +func AdjustWindowRect(rect *RECT, style uint, menu bool) bool { + ret, _, _ := procAdjustWindowRect.Call( + uintptr(unsafe.Pointer(rect)), + uintptr(style), + uintptr(BoolToBOOL(menu))) + + return ret != 0 +} + +func DestroyWindow(hwnd HWND) bool { + ret, _, _ := procDestroyWindow.Call(hwnd) + return ret != 0 +} + +func HasGetDpiForWindowFunc() bool { + err := procGetDpiForWindow.Find() + return err == nil +} + +func GetDpiForWindow(hwnd HWND) UINT { + dpi, _, _ := procGetDpiForWindow.Call(hwnd) + return uint(dpi) +} + +func SetWindowCompositionAttribute(hwnd HWND, data *WINDOWCOMPOSITIONATTRIBDATA) bool { + if procSetWindowCompositionAttribute != nil { + ret, _, _ := procSetWindowCompositionAttribute.Call( + hwnd, + uintptr(unsafe.Pointer(data)), + ) + return ret != 0 + } + return false +} + +func DefWindowProc(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr { + ret, _, _ := procDefWindowProc.Call( + uintptr(hwnd), + uintptr(msg), + wParam, + lParam) + + return ret +} + +func DefDlgProc(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr { + ret, _, _ := procDefDlgProc.Call( + uintptr(hwnd), + uintptr(msg), + wParam, + lParam) + + return ret +} + +func PostQuitMessage(exitCode int) { + procPostQuitMessage.Call( + uintptr(exitCode)) +} + +func GetMessage(msg *MSG, hwnd HWND, msgFilterMin, msgFilterMax uint32) int { + ret, _, _ := procGetMessage.Call( + uintptr(unsafe.Pointer(msg)), + uintptr(hwnd), + uintptr(msgFilterMin), + uintptr(msgFilterMax)) + + return int(ret) +} + +func TranslateMessage(msg *MSG) bool { + ret, _, _ := procTranslateMessage.Call( + uintptr(unsafe.Pointer(msg))) + + return ret != 0 + +} + +func DispatchMessage(msg *MSG) uintptr { + ret, _, _ := procDispatchMessage.Call( + uintptr(unsafe.Pointer(msg))) + + return ret + +} + +func SendMessage(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr { + ret, _, _ := procSendMessage.Call( + uintptr(hwnd), + uintptr(msg), + wParam, + lParam) + + return ret +} + +func PostMessage(hwnd HWND, msg uint32, wParam, lParam uintptr) bool { + ret, _, _ := procPostMessage.Call( + uintptr(hwnd), + uintptr(msg), + wParam, + lParam) + + return ret != 0 +} + +func WaitMessage() bool { + ret, _, _ := procWaitMessage.Call() + return ret != 0 +} + +func SetWindowText(hwnd HWND, text string) { + procSetWindowText.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text)))) +} + +func GetWindowTextLength(hwnd HWND) int { + ret, _, _ := procGetWindowTextLength.Call( + uintptr(hwnd)) + + return int(ret) +} + +func GetWindowInfo(hwnd HWND, info *WINDOWINFO) int { + ret, _, _ := procGetWindowInfo.Call( + hwnd, + uintptr(unsafe.Pointer(info)), + ) + return int(ret) +} + +func GetWindowText(hwnd HWND) string { + textLen := GetWindowTextLength(hwnd) + 1 + + buf := make([]uint16, textLen) + procGetWindowText.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(textLen)) + + return syscall.UTF16ToString(buf) +} + +func GetWindowRect(hwnd HWND) *RECT { + var rect RECT + procGetWindowRect.Call( + hwnd, + uintptr(unsafe.Pointer(&rect))) + + return &rect +} + +func MoveWindow(hwnd HWND, x, y, width, height int, repaint bool) bool { + ret, _, _ := procMoveWindow.Call( + uintptr(hwnd), + uintptr(x), + uintptr(y), + uintptr(width), + uintptr(height), + uintptr(BoolToBOOL(repaint))) + + return ret != 0 + +} + +func ScreenToClient(hwnd HWND, x, y int) (X, Y int, ok bool) { + pt := POINT{X: int32(x), Y: int32(y)} + ret, _, _ := procScreenToClient.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(&pt))) + + return int(pt.X), int(pt.Y), ret != 0 +} + +func CallWindowProc(preWndProc uintptr, hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr { + ret, _, _ := procCallWindowProc.Call( + preWndProc, + uintptr(hwnd), + uintptr(msg), + wParam, + lParam) + + return ret +} + +func SetWindowLong(hwnd HWND, index int, value uint32) uint32 { + ret, _, _ := procSetWindowLong.Call( + uintptr(hwnd), + uintptr(index), + uintptr(value)) + + return uint32(ret) +} + +func SetWindowLongPtr(hwnd HWND, index int, value uintptr) uintptr { + ret, _, _ := procSetWindowLongPtr.Call( + uintptr(hwnd), + uintptr(index), + value) + + return ret +} + +func GetWindowLong(hwnd HWND, index int) int32 { + ret, _, _ := procGetWindowLong.Call( + uintptr(hwnd), + uintptr(index)) + + return int32(ret) +} + +func GetWindowLongPtr(hwnd HWND, index int) uintptr { + ret, _, _ := procGetWindowLongPtr.Call( + uintptr(hwnd), + uintptr(index)) + + return ret +} + +func EnableWindow(hwnd HWND, b bool) bool { + ret, _, _ := procEnableWindow.Call( + uintptr(hwnd), + uintptr(BoolToBOOL(b))) + return ret != 0 +} + +func IsWindowEnabled(hwnd HWND) bool { + ret, _, _ := procIsWindowEnabled.Call( + uintptr(hwnd)) + + return ret != 0 +} + +func IsWindowVisible(hwnd HWND) bool { + ret, _, _ := procIsWindowVisible.Call( + uintptr(hwnd)) + + return ret != 0 +} + +func SetFocus(hwnd HWND) HWND { + ret, _, _ := procSetFocus.Call( + uintptr(hwnd)) + + return HWND(ret) +} + +func SetActiveWindow(hwnd HWND) HWND { + ret, _, _ := procSetActiveWindow.Call( + uintptr(hwnd)) + + return HWND(ret) +} + +func BringWindowToTop(hwnd HWND) bool { + ret, _, _ := procBringWindowToTop.Call(uintptr(hwnd)) + return ret != 0 +} + +func SetForegroundWindow(hwnd HWND) HWND { + ret, _, _ := procSetForegroundWindow.Call( + uintptr(hwnd)) + + return HWND(ret) +} + +func GetFocus() HWND { + ret, _, _ := procGetFocus.Call() + return HWND(ret) +} + +func InvalidateRect(hwnd HWND, rect *RECT, erase bool) bool { + ret, _, _ := procInvalidateRect.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(rect)), + uintptr(BoolToBOOL(erase))) + + return ret != 0 +} + +func GetClientRect(hwnd HWND) *RECT { + var rect RECT + ret, _, _ := procGetClientRect.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(&rect))) + + if ret == 0 { + panic(fmt.Sprintf("GetClientRect(%d) failed", hwnd)) + } + + return &rect +} + +func GetDC(hwnd HWND) HDC { + ret, _, _ := procGetDC.Call( + uintptr(hwnd)) + + return HDC(ret) +} + +func ReleaseDC(hwnd HWND, hDC HDC) bool { + ret, _, _ := procReleaseDC.Call( + uintptr(hwnd), + uintptr(hDC)) + + return ret != 0 +} + +func SetCapture(hwnd HWND) HWND { + ret, _, _ := procSetCapture.Call( + uintptr(hwnd)) + + return HWND(ret) +} + +func ReleaseCapture() bool { + ret, _, _ := procReleaseCapture.Call() + + return ret != 0 +} + +func GetWindowThreadProcessId(hwnd HWND) (HANDLE, int) { + var processId int + ret, _, _ := procGetWindowThreadProcessId.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(&processId))) + + return HANDLE(ret), processId +} + +func MessageBox(hwnd HWND, title, caption string, flags uint) int { + ret, _, _ := procMessageBox.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(title))), + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(caption))), + uintptr(flags)) + + return int(ret) +} + +func GetSystemMetrics(index int) int { + ret, _, _ := procGetSystemMetrics.Call( + uintptr(index)) + + return int(ret) +} + +func GetSysColorBrush(nIndex int) HBRUSH { + /* + ret, _, _ := procSysColorBrush.Call(1, + uintptr(nIndex), + 0, + 0) + + return HBRUSH(ret) + */ + ret, _, _ := syscall.Syscall(getSysColorBrush, 1, + uintptr(nIndex), + 0, + 0) + + return HBRUSH(ret) +} + +func CopyRect(dst, src *RECT) bool { + ret, _, _ := procCopyRect.Call( + uintptr(unsafe.Pointer(dst)), + uintptr(unsafe.Pointer(src))) + + return ret != 0 +} + +func EqualRect(rect1, rect2 *RECT) bool { + ret, _, _ := procEqualRect.Call( + uintptr(unsafe.Pointer(rect1)), + uintptr(unsafe.Pointer(rect2))) + + return ret != 0 +} + +func InflateRect(rect *RECT, dx, dy int) bool { + ret, _, _ := procInflateRect.Call( + uintptr(unsafe.Pointer(rect)), + uintptr(dx), + uintptr(dy)) + + return ret != 0 +} + +func IntersectRect(dst, src1, src2 *RECT) bool { + ret, _, _ := procIntersectRect.Call( + uintptr(unsafe.Pointer(dst)), + uintptr(unsafe.Pointer(src1)), + uintptr(unsafe.Pointer(src2))) + + return ret != 0 +} + +func IsRectEmpty(rect *RECT) bool { + ret, _, _ := procIsRectEmpty.Call( + uintptr(unsafe.Pointer(rect))) + + return ret != 0 +} + +func OffsetRect(rect *RECT, dx, dy int) bool { + ret, _, _ := procOffsetRect.Call( + uintptr(unsafe.Pointer(rect)), + uintptr(dx), + uintptr(dy)) + + return ret != 0 +} + +func PtInRect(rect *RECT, x, y int) bool { + pt := POINT{X: int32(x), Y: int32(y)} + ret, _, _ := procPtInRect.Call( + uintptr(unsafe.Pointer(rect)), + uintptr(unsafe.Pointer(&pt))) + + return ret != 0 +} + +func SetRect(rect *RECT, left, top, right, bottom int) bool { + ret, _, _ := procSetRect.Call( + uintptr(unsafe.Pointer(rect)), + uintptr(left), + uintptr(top), + uintptr(right), + uintptr(bottom)) + + return ret != 0 +} + +func SetRectEmpty(rect *RECT) bool { + ret, _, _ := procSetRectEmpty.Call( + uintptr(unsafe.Pointer(rect))) + + return ret != 0 +} + +func SubtractRect(dst, src1, src2 *RECT) bool { + ret, _, _ := procSubtractRect.Call( + uintptr(unsafe.Pointer(dst)), + uintptr(unsafe.Pointer(src1)), + uintptr(unsafe.Pointer(src2))) + + return ret != 0 +} + +func UnionRect(dst, src1, src2 *RECT) bool { + ret, _, _ := procUnionRect.Call( + uintptr(unsafe.Pointer(dst)), + uintptr(unsafe.Pointer(src1)), + uintptr(unsafe.Pointer(src2))) + + return ret != 0 +} + +func CreateDialog(hInstance HINSTANCE, lpTemplate *uint16, hWndParent HWND, lpDialogProc uintptr) HWND { + ret, _, _ := procCreateDialogParam.Call( + uintptr(hInstance), + uintptr(unsafe.Pointer(lpTemplate)), + uintptr(hWndParent), + lpDialogProc, + 0) + + return HWND(ret) +} + +func DialogBox(hInstance HINSTANCE, lpTemplateName *uint16, hWndParent HWND, lpDialogProc uintptr) int { + ret, _, _ := procDialogBoxParam.Call( + uintptr(hInstance), + uintptr(unsafe.Pointer(lpTemplateName)), + uintptr(hWndParent), + lpDialogProc, + 0) + + return int(ret) +} + +func GetDlgItem(hDlg HWND, nIDDlgItem int) HWND { + ret, _, _ := procGetDlgItem.Call( + uintptr(unsafe.Pointer(hDlg)), + uintptr(nIDDlgItem)) + + return HWND(ret) +} + +func DrawIcon(hDC HDC, x, y int, hIcon HICON) bool { + ret, _, _ := procDrawIcon.Call( + uintptr(unsafe.Pointer(hDC)), + uintptr(x), + uintptr(y), + uintptr(unsafe.Pointer(hIcon))) + + return ret != 0 +} + +func CreateMenu() HMENU { + ret, _, _ := procCreateMenu.Call(0, + 0, + 0, + 0) + + return HMENU(ret) +} + +func SetMenu(hWnd HWND, hMenu HMENU) bool { + ret, _, _ := syscall.Syscall(setMenu, 2, + uintptr(hWnd), + uintptr(hMenu), + 0) + + return ret != 0 +} + +// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-checkmenuradioitem +func SelectRadioMenuItem(menuID uint16, startID uint16, endID uint16, hwnd HWND) bool { + ret, _, _ := procCheckMenuRadioItem.Call( + hwnd, + uintptr(startID), + uintptr(endID), + uintptr(menuID), + MF_BYCOMMAND) + return ret != 0 + +} + +func CreatePopupMenu() HMENU { + ret, _, _ := procCreatePopupMenu.Call(0, + 0, + 0, + 0) + + return HMENU(ret) +} + +func TrackPopupMenuEx(hMenu HMENU, fuFlags uint32, x, y int32, hWnd HWND, lptpm *TPMPARAMS) BOOL { + ret, _, _ := syscall.Syscall6(trackPopupMenuEx, 6, + uintptr(hMenu), + uintptr(fuFlags), + uintptr(x), + uintptr(y), + uintptr(hWnd), + uintptr(unsafe.Pointer(lptpm))) + + return BOOL(ret) +} + +func DrawMenuBar(hWnd HWND) bool { + ret, _, _ := syscall.Syscall(drawMenuBar, 1, + uintptr(hWnd), + 0, + 0) + + return ret != 0 +} + +func InsertMenuItem(hMenu HMENU, uItem uint32, fByPosition bool, lpmii *MENUITEMINFO) bool { + ret, _, _ := syscall.Syscall6(insertMenuItem, 4, + uintptr(hMenu), + uintptr(uItem), + uintptr(BoolToBOOL(fByPosition)), + uintptr(unsafe.Pointer(lpmii)), + 0, + 0) + + return ret != 0 +} + +func SetMenuItemInfo(hMenu HMENU, uItem uint32, fByPosition bool, lpmii *MENUITEMINFO) bool { + ret, _, _ := syscall.Syscall6(setMenuItemInfo, 4, + uintptr(hMenu), + uintptr(uItem), + uintptr(BoolToBOOL(fByPosition)), + uintptr(unsafe.Pointer(lpmii)), + 0, + 0) + + return ret != 0 +} + +func ClientToScreen(hwnd HWND, x, y int) (int, int) { + pt := POINT{X: int32(x), Y: int32(y)} + + procClientToScreen.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(&pt))) + + return int(pt.X), int(pt.Y) +} + +func IsDialogMessage(hwnd HWND, msg *MSG) bool { + ret, _, _ := procIsDialogMessage.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(msg))) + + return ret != 0 +} + +func IsWindow(hwnd HWND) bool { + ret, _, _ := procIsWindow.Call( + uintptr(hwnd)) + + return ret != 0 +} + +func EndDialog(hwnd HWND, nResult uintptr) bool { + ret, _, _ := procEndDialog.Call( + uintptr(hwnd), + nResult) + + return ret != 0 +} + +func PeekMessage(lpMsg *MSG, hwnd HWND, wMsgFilterMin, wMsgFilterMax, wRemoveMsg uint32) bool { + ret, _, _ := procPeekMessage.Call( + uintptr(unsafe.Pointer(lpMsg)), + uintptr(hwnd), + uintptr(wMsgFilterMin), + uintptr(wMsgFilterMax), + uintptr(wRemoveMsg)) + + return ret != 0 +} + +func TranslateAccelerator(hwnd HWND, hAccTable HACCEL, lpMsg *MSG) bool { + ret, _, _ := procTranslateMessage.Call( + uintptr(hwnd), + uintptr(hAccTable), + uintptr(unsafe.Pointer(lpMsg))) + + return ret != 0 +} + +func SetWindowPos(hwnd, hWndInsertAfter HWND, x, y, cx, cy int, uFlags uint) bool { + ret, _, _ := procSetWindowPos.Call( + uintptr(hwnd), + uintptr(hWndInsertAfter), + uintptr(x), + uintptr(y), + uintptr(cx), + uintptr(cy), + uintptr(uFlags)) + + return ret != 0 +} + +func FillRect(hDC HDC, lprc *RECT, hbr HBRUSH) bool { + ret, _, _ := procFillRect.Call( + uintptr(hDC), + uintptr(unsafe.Pointer(lprc)), + uintptr(hbr)) + + return ret != 0 +} + +func DrawText(hDC HDC, text string, uCount int, lpRect *RECT, uFormat uint) int { + ret, _, _ := procDrawText.Call( + uintptr(hDC), + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))), + uintptr(uCount), + uintptr(unsafe.Pointer(lpRect)), + uintptr(uFormat)) + + return int(ret) +} + +func AddClipboardFormatListener(hwnd HWND) bool { + ret, _, _ := procAddClipboardFormatListener.Call( + uintptr(hwnd)) + return ret != 0 +} + +func RemoveClipboardFormatListener(hwnd HWND) bool { + ret, _, _ := procRemoveClipboardFormatListener.Call( + uintptr(hwnd)) + return ret != 0 +} + +func OpenClipboard(hWndNewOwner HWND) bool { + ret, _, _ := procOpenClipboard.Call( + uintptr(hWndNewOwner)) + return ret != 0 +} + +func CloseClipboard() bool { + ret, _, _ := procCloseClipboard.Call() + return ret != 0 +} + +func EnumClipboardFormats(format uint) uint { + ret, _, _ := procEnumClipboardFormats.Call( + uintptr(format)) + return uint(ret) +} + +func GetClipboardData(uFormat uint) HANDLE { + ret, _, _ := procGetClipboardData.Call( + uintptr(uFormat)) + return HANDLE(ret) +} + +func SetClipboardData(uFormat uint, hMem HANDLE) HANDLE { + ret, _, _ := procSetClipboardData.Call( + uintptr(uFormat), + uintptr(hMem)) + return HANDLE(ret) +} + +func EmptyClipboard() bool { + ret, _, _ := procEmptyClipboard.Call() + return ret != 0 +} + +func GetClipboardFormatName(format uint) (string, bool) { + cchMaxCount := 255 + buf := make([]uint16, cchMaxCount) + ret, _, _ := procGetClipboardFormatName.Call( + uintptr(format), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(cchMaxCount)) + + if ret > 0 { + return syscall.UTF16ToString(buf), true + } + + return "Requested format does not exist or is predefined", false +} + +func IsClipboardFormatAvailable(format uint) bool { + ret, _, _ := procIsClipboardFormatAvailable.Call(uintptr(format)) + return ret != 0 +} + +func BeginPaint(hwnd HWND, paint *PAINTSTRUCT) HDC { + ret, _, _ := procBeginPaint.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(paint))) + return HDC(ret) +} + +func EndPaint(hwnd HWND, paint *PAINTSTRUCT) { + procEndPaint.Call( + uintptr(hwnd), + uintptr(unsafe.Pointer(paint))) +} + +func GetKeyboardState(lpKeyState *[]byte) bool { + ret, _, _ := procGetKeyboardState.Call( + uintptr(unsafe.Pointer(&(*lpKeyState)[0]))) + return ret != 0 +} + +func MapVirtualKeyEx(uCode, uMapType uint, dwhkl HKL) uint { + ret, _, _ := procMapVirtualKey.Call( + uintptr(uCode), + uintptr(uMapType), + uintptr(dwhkl)) + return uint(ret) +} + +func GetAsyncKeyState(vKey int) uint16 { + ret, _, _ := procGetAsyncKeyState.Call(uintptr(vKey)) + return uint16(ret) +} + +func ToAscii(uVirtKey, uScanCode uint, lpKeyState *byte, lpChar *uint16, uFlags uint) int { + ret, _, _ := procToAscii.Call( + uintptr(uVirtKey), + uintptr(uScanCode), + uintptr(unsafe.Pointer(lpKeyState)), + uintptr(unsafe.Pointer(lpChar)), + uintptr(uFlags)) + return int(ret) +} + +func SwapMouseButton(fSwap bool) bool { + ret, _, _ := procSwapMouseButton.Call( + uintptr(BoolToBOOL(fSwap))) + return ret != 0 +} + +func GetCursorPos() (x, y int, ok bool) { + pt := POINT{} + ret, _, _ := procGetCursorPos.Call(uintptr(unsafe.Pointer(&pt))) + return int(pt.X), int(pt.Y), ret != 0 +} + +func SetCursorPos(x, y int) bool { + ret, _, _ := procSetCursorPos.Call( + uintptr(x), + uintptr(y), + ) + return ret != 0 +} + +func SetCursor(cursor HCURSOR) HCURSOR { + ret, _, _ := procSetCursor.Call( + uintptr(cursor), + ) + return HCURSOR(ret) +} + +func CreateIcon(instance HINSTANCE, nWidth, nHeight int, cPlanes, cBitsPerPixel byte, ANDbits, XORbits *byte) HICON { + ret, _, _ := procCreateIcon.Call( + uintptr(instance), + uintptr(nWidth), + uintptr(nHeight), + uintptr(cPlanes), + uintptr(cBitsPerPixel), + uintptr(unsafe.Pointer(ANDbits)), + uintptr(unsafe.Pointer(XORbits)), + ) + return HICON(ret) +} + +func DestroyIcon(icon HICON) bool { + ret, _, _ := procDestroyIcon.Call( + uintptr(icon), + ) + return ret != 0 +} + +func MonitorFromPoint(x, y int, dwFlags uint32) HMONITOR { + ret, _, _ := procMonitorFromPoint.Call( + uintptr(x), + uintptr(y), + uintptr(dwFlags), + ) + return HMONITOR(ret) +} + +func MonitorFromRect(rc *RECT, dwFlags uint32) HMONITOR { + ret, _, _ := procMonitorFromRect.Call( + uintptr(unsafe.Pointer(rc)), + uintptr(dwFlags), + ) + return HMONITOR(ret) +} + +func MonitorFromWindow(hwnd HWND, dwFlags uint32) HMONITOR { + ret, _, _ := procMonitorFromWindow.Call( + uintptr(hwnd), + uintptr(dwFlags), + ) + return HMONITOR(ret) +} + +func GetMonitorInfo(hMonitor HMONITOR, lmpi *MONITORINFO) bool { + ret, _, _ := procGetMonitorInfo.Call( + uintptr(hMonitor), + uintptr(unsafe.Pointer(lmpi)), + ) + return ret != 0 +} + +func EnumDisplayMonitors(hdc HDC, clip *RECT, fnEnum, dwData uintptr) bool { + ret, _, _ := procEnumDisplayMonitors.Call( + uintptr(hdc), + uintptr(unsafe.Pointer(clip)), + fnEnum, + dwData, + ) + return ret != 0 +} + +func EnumDisplaySettingsEx(szDeviceName *uint16, iModeNum uint32, devMode *DEVMODE, dwFlags uint32) bool { + ret, _, _ := procEnumDisplaySettingsEx.Call( + uintptr(unsafe.Pointer(szDeviceName)), + uintptr(iModeNum), + uintptr(unsafe.Pointer(devMode)), + uintptr(dwFlags), + ) + return ret != 0 +} + +func ChangeDisplaySettingsEx(szDeviceName *uint16, devMode *DEVMODE, hwnd HWND, dwFlags uint32, lParam uintptr) int32 { + ret, _, _ := procChangeDisplaySettingsEx.Call( + uintptr(unsafe.Pointer(szDeviceName)), + uintptr(unsafe.Pointer(devMode)), + uintptr(hwnd), + uintptr(dwFlags), + lParam, + ) + return int32(ret) +} + +/* +func SendInput(inputs []INPUT) uint32 { + var validInputs []C.INPUT + + for _, oneInput := range inputs { + input := C.INPUT{_type: C.DWORD(oneInput.Type)} + + switch oneInput.Type { + case INPUT_MOUSE: + (*MouseInput)(unsafe.Pointer(&input)).mi = oneInput.Mi + case INPUT_KEYBOARD: + (*KbdInput)(unsafe.Pointer(&input)).ki = oneInput.Ki + case INPUT_HARDWARE: + (*HardwareInput)(unsafe.Pointer(&input)).hi = oneInput.Hi + default: + panic("unkown type") + } + + validInputs = append(validInputs, input) + } + + ret, _, _ := procSendInput.Call( + uintptr(len(validInputs)), + uintptr(unsafe.Pointer(&validInputs[0])), + uintptr(unsafe.Sizeof(C.INPUT{})), + ) + return uint32(ret) +}*/ + +func SetWindowsHookEx(idHook int, lpfn HOOKPROC, hMod HINSTANCE, dwThreadId DWORD) HHOOK { + ret, _, _ := procSetWindowsHookEx.Call( + uintptr(idHook), + uintptr(syscall.NewCallback(lpfn)), + uintptr(hMod), + uintptr(dwThreadId), + ) + return HHOOK(ret) +} + +func UnhookWindowsHookEx(hhk HHOOK) bool { + ret, _, _ := procUnhookWindowsHookEx.Call( + uintptr(hhk), + ) + return ret != 0 +} + +func CallNextHookEx(hhk HHOOK, nCode int, wParam WPARAM, lParam LPARAM) LRESULT { + ret, _, _ := procCallNextHookEx.Call( + uintptr(hhk), + uintptr(nCode), + uintptr(wParam), + uintptr(lParam), + ) + return LRESULT(ret) +} + +func GetKeyState(nVirtKey int32) int16 { + ret, _, _ := syscall.Syscall(getKeyState, 1, + uintptr(nVirtKey), + 0, + 0) + + return int16(ret) +} + +func DestroyMenu(hMenu HMENU) bool { + ret, _, _ := procDestroyMenu.Call(1, + uintptr(hMenu), + 0, + 0) + + return ret != 0 +} + +func GetWindowPlacement(hWnd HWND, lpwndpl *WINDOWPLACEMENT) bool { + ret, _, _ := syscall.Syscall(getWindowPlacement, 2, + uintptr(hWnd), + uintptr(unsafe.Pointer(lpwndpl)), + 0) + + return ret != 0 +} + +func SetWindowPlacement(hWnd HWND, lpwndpl *WINDOWPLACEMENT) bool { + ret, _, _ := syscall.Syscall(setWindowPlacement, 2, + uintptr(hWnd), + uintptr(unsafe.Pointer(lpwndpl)), + 0) + + return ret != 0 +} + +func SetScrollInfo(hwnd HWND, fnBar int32, lpsi *SCROLLINFO, fRedraw bool) int32 { + ret, _, _ := syscall.Syscall6(setScrollInfo, 4, + hwnd, + uintptr(fnBar), + uintptr(unsafe.Pointer(lpsi)), + uintptr(BoolToBOOL(fRedraw)), + 0, + 0) + + return int32(ret) +} + +func GetScrollInfo(hwnd HWND, fnBar int32, lpsi *SCROLLINFO) bool { + ret, _, _ := syscall.Syscall(getScrollInfo, 3, + hwnd, + uintptr(fnBar), + uintptr(unsafe.Pointer(lpsi))) + + return ret != 0 +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/utils.go b/v2/internal/frontend/desktop/windows/winc/w32/utils.go new file mode 100644 index 000000000..cb8d354d0 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/utils.go @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2019 Tad Vizbaras. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +import ( + "syscall" + "unicode/utf16" + "unsafe" +) + +func MustLoadLibrary(name string) uintptr { + lib, err := syscall.LoadLibrary(name) + if err != nil { + panic(err) + } + + return uintptr(lib) +} + +func MustGetProcAddress(lib uintptr, name string) uintptr { + addr, err := syscall.GetProcAddress(syscall.Handle(lib), name) + if err != nil { + panic(err) + } + + return uintptr(addr) +} + +func SUCCEEDED(hr HRESULT) bool { + return hr >= 0 +} + +func FAILED(hr HRESULT) bool { + return hr < 0 +} + +func MakeIntResource(id uint16) *uint16 { + return (*uint16)(unsafe.Pointer(uintptr(id))) +} + +func LOWORD(dw uint32) uint16 { + return uint16(dw) +} + +func HIWORD(dw uint32) uint16 { + return uint16(dw >> 16 & 0xffff) +} + +func MAKELONG(lo, hi uint16) uint32 { + return uint32(uint32(lo) | ((uint32(hi)) << 16)) +} + +func BoolToBOOL(value bool) BOOL { + if value { + return 1 + } + + return 0 +} + +func UTF16PtrToString(cstr *uint16) string { + if cstr != nil { + us := make([]uint16, 0, 256) + for p := uintptr(unsafe.Pointer(cstr)); ; p += 2 { + u := *(*uint16)(unsafe.Pointer(p)) + if u == 0 { + return string(utf16.Decode(us)) + } + us = append(us, u) + } + } + + return "" +} + +func ComAddRef(unknown *IUnknown) int32 { + ret, _, _ := syscall.Syscall(unknown.lpVtbl.pAddRef, 1, + uintptr(unsafe.Pointer(unknown)), + 0, + 0) + return int32(ret) +} + +func ComRelease(unknown *IUnknown) int32 { + ret, _, _ := syscall.Syscall(unknown.lpVtbl.pRelease, 1, + uintptr(unsafe.Pointer(unknown)), + 0, + 0) + return int32(ret) +} + +func ComQueryInterface(unknown *IUnknown, id *GUID) *IDispatch { + var disp *IDispatch + hr, _, _ := syscall.Syscall(unknown.lpVtbl.pQueryInterface, 3, + uintptr(unsafe.Pointer(unknown)), + uintptr(unsafe.Pointer(id)), + uintptr(unsafe.Pointer(&disp))) + if hr != 0 { + panic("Invoke QieryInterface error.") + } + return disp +} + +func ComGetIDsOfName(disp *IDispatch, names []string) []int32 { + wnames := make([]*uint16, len(names)) + dispid := make([]int32, len(names)) + for i := 0; i < len(names); i++ { + wnames[i] = syscall.StringToUTF16Ptr(names[i]) + } + hr, _, _ := syscall.Syscall6(disp.lpVtbl.pGetIDsOfNames, 6, + uintptr(unsafe.Pointer(disp)), + uintptr(unsafe.Pointer(IID_NULL)), + uintptr(unsafe.Pointer(&wnames[0])), + uintptr(len(names)), + uintptr(GetUserDefaultLCID()), + uintptr(unsafe.Pointer(&dispid[0]))) + if hr != 0 { + panic("Invoke GetIDsOfName error.") + } + return dispid +} + +func ComInvoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (result *VARIANT) { + var dispparams DISPPARAMS + + if dispatch&DISPATCH_PROPERTYPUT != 0 { + dispnames := [1]int32{DISPID_PROPERTYPUT} + dispparams.RgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0])) + dispparams.CNamedArgs = 1 + } + var vargs []VARIANT + if len(params) > 0 { + vargs = make([]VARIANT, len(params)) + for i, v := range params { + //n := len(params)-i-1 + n := len(params) - i - 1 + VariantInit(&vargs[n]) + switch v.(type) { + case bool: + if v.(bool) { + vargs[n] = VARIANT{VT_BOOL, 0, 0, 0, 0xffff} + } else { + vargs[n] = VARIANT{VT_BOOL, 0, 0, 0, 0} + } + case *bool: + vargs[n] = VARIANT{VT_BOOL | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*bool))))} + case byte: + vargs[n] = VARIANT{VT_I1, 0, 0, 0, int64(v.(byte))} + case *byte: + vargs[n] = VARIANT{VT_I1 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*byte))))} + case int16: + vargs[n] = VARIANT{VT_I2, 0, 0, 0, int64(v.(int16))} + case *int16: + vargs[n] = VARIANT{VT_I2 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*int16))))} + case uint16: + vargs[n] = VARIANT{VT_UI2, 0, 0, 0, int64(v.(int16))} + case *uint16: + vargs[n] = VARIANT{VT_UI2 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*uint16))))} + case int, int32: + vargs[n] = VARIANT{VT_UI4, 0, 0, 0, int64(v.(int))} + case *int, *int32: + vargs[n] = VARIANT{VT_I4 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*int))))} + case uint, uint32: + vargs[n] = VARIANT{VT_UI4, 0, 0, 0, int64(v.(uint))} + case *uint, *uint32: + vargs[n] = VARIANT{VT_UI4 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*uint))))} + case int64: + vargs[n] = VARIANT{VT_I8, 0, 0, 0, v.(int64)} + case *int64: + vargs[n] = VARIANT{VT_I8 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*int64))))} + case uint64: + vargs[n] = VARIANT{VT_UI8, 0, 0, 0, int64(v.(uint64))} + case *uint64: + vargs[n] = VARIANT{VT_UI8 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*uint64))))} + case float32: + vargs[n] = VARIANT{VT_R4, 0, 0, 0, int64(v.(float32))} + case *float32: + vargs[n] = VARIANT{VT_R4 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*float32))))} + case float64: + vargs[n] = VARIANT{VT_R8, 0, 0, 0, int64(v.(float64))} + case *float64: + vargs[n] = VARIANT{VT_R8 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*float64))))} + case string: + vargs[n] = VARIANT{VT_BSTR, 0, 0, 0, int64(uintptr(unsafe.Pointer(SysAllocString(v.(string)))))} + case *string: + vargs[n] = VARIANT{VT_BSTR | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*string))))} + case *IDispatch: + vargs[n] = VARIANT{VT_DISPATCH, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*IDispatch))))} + case **IDispatch: + vargs[n] = VARIANT{VT_DISPATCH | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(**IDispatch))))} + case nil: + vargs[n] = VARIANT{VT_NULL, 0, 0, 0, 0} + case *VARIANT: + vargs[n] = VARIANT{VT_VARIANT | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*VARIANT))))} + default: + panic("unknown type") + } + } + dispparams.Rgvarg = uintptr(unsafe.Pointer(&vargs[0])) + dispparams.CArgs = uint32(len(params)) + } + + var ret VARIANT + var excepInfo EXCEPINFO + VariantInit(&ret) + hr, _, _ := syscall.Syscall9(disp.lpVtbl.pInvoke, 8, + uintptr(unsafe.Pointer(disp)), + uintptr(dispid), + uintptr(unsafe.Pointer(IID_NULL)), + uintptr(GetUserDefaultLCID()), + uintptr(dispatch), + uintptr(unsafe.Pointer(&dispparams)), + uintptr(unsafe.Pointer(&ret)), + uintptr(unsafe.Pointer(&excepInfo)), + 0) + if hr != 0 { + if excepInfo.BstrDescription != nil { + bs := UTF16PtrToString(excepInfo.BstrDescription) + panic(bs) + } + } + for _, varg := range vargs { + if varg.VT == VT_BSTR && varg.Val != 0 { + SysFreeString(((*int16)(unsafe.Pointer(uintptr(varg.Val))))) + } + } + result = &ret + return +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/uxtheme.go b/v2/internal/frontend/desktop/windows/winc/w32/uxtheme.go new file mode 100644 index 000000000..67b028722 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/uxtheme.go @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2019 Tad Vizbaras. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +import ( + "syscall" + "unsafe" +) + +// LISTVIEW parts +const ( + LVP_LISTITEM = 1 + LVP_LISTGROUP = 2 + LVP_LISTDETAIL = 3 + LVP_LISTSORTEDDETAIL = 4 + LVP_EMPTYTEXT = 5 + LVP_GROUPHEADER = 6 + LVP_GROUPHEADERLINE = 7 + LVP_EXPANDBUTTON = 8 + LVP_COLLAPSEBUTTON = 9 + LVP_COLUMNDETAIL = 10 +) + +// LVP_LISTITEM states +const ( + LISS_NORMAL = 1 + LISS_HOT = 2 + LISS_SELECTED = 3 + LISS_DISABLED = 4 + LISS_SELECTEDNOTFOCUS = 5 + LISS_HOTSELECTED = 6 +) + +// TREEVIEW parts +const ( + TVP_TREEITEM = 1 + TVP_GLYPH = 2 + TVP_BRANCH = 3 + TVP_HOTGLYPH = 4 +) + +// TVP_TREEITEM states +const ( + TREIS_NORMAL = 1 + TREIS_HOT = 2 + TREIS_SELECTED = 3 + TREIS_DISABLED = 4 + TREIS_SELECTEDNOTFOCUS = 5 + TREIS_HOTSELECTED = 6 +) + +type HTHEME HANDLE + +var ( + // Library + libuxtheme uintptr + + // Functions + closeThemeData uintptr + drawThemeBackground uintptr + drawThemeText uintptr + getThemeTextExtent uintptr + openThemeData uintptr + setWindowTheme uintptr +) + +func init() { + // Library + libuxtheme = MustLoadLibrary("uxtheme.dll") + + // Functions + closeThemeData = MustGetProcAddress(libuxtheme, "CloseThemeData") + drawThemeBackground = MustGetProcAddress(libuxtheme, "DrawThemeBackground") + drawThemeText = MustGetProcAddress(libuxtheme, "DrawThemeText") + getThemeTextExtent = MustGetProcAddress(libuxtheme, "GetThemeTextExtent") + openThemeData = MustGetProcAddress(libuxtheme, "OpenThemeData") + setWindowTheme = MustGetProcAddress(libuxtheme, "SetWindowTheme") +} + +func CloseThemeData(hTheme HTHEME) HRESULT { + ret, _, _ := syscall.Syscall(closeThemeData, 1, + uintptr(hTheme), + 0, + 0) + + return HRESULT(ret) +} + +func DrawThemeBackground(hTheme HTHEME, hdc HDC, iPartId, iStateId int32, pRect, pClipRect *RECT) HRESULT { + ret, _, _ := syscall.Syscall6(drawThemeBackground, 6, + uintptr(hTheme), + uintptr(hdc), + uintptr(iPartId), + uintptr(iStateId), + uintptr(unsafe.Pointer(pRect)), + uintptr(unsafe.Pointer(pClipRect))) + + return HRESULT(ret) +} + +func DrawThemeText(hTheme HTHEME, hdc HDC, iPartId, iStateId int32, pszText *uint16, iCharCount int32, dwTextFlags, dwTextFlags2 uint32, pRect *RECT) HRESULT { + ret, _, _ := syscall.Syscall9(drawThemeText, 9, + uintptr(hTheme), + uintptr(hdc), + uintptr(iPartId), + uintptr(iStateId), + uintptr(unsafe.Pointer(pszText)), + uintptr(iCharCount), + uintptr(dwTextFlags), + uintptr(dwTextFlags2), + uintptr(unsafe.Pointer(pRect))) + + return HRESULT(ret) +} + +func GetThemeTextExtent(hTheme HTHEME, hdc HDC, iPartId, iStateId int32, pszText *uint16, iCharCount int32, dwTextFlags uint32, pBoundingRect, pExtentRect *RECT) HRESULT { + ret, _, _ := syscall.Syscall9(getThemeTextExtent, 9, + uintptr(hTheme), + uintptr(hdc), + uintptr(iPartId), + uintptr(iStateId), + uintptr(unsafe.Pointer(pszText)), + uintptr(iCharCount), + uintptr(dwTextFlags), + uintptr(unsafe.Pointer(pBoundingRect)), + uintptr(unsafe.Pointer(pExtentRect))) + + return HRESULT(ret) +} + +func OpenThemeData(hwnd HWND, pszClassList *uint16) HTHEME { + ret, _, _ := syscall.Syscall(openThemeData, 2, + uintptr(hwnd), + uintptr(unsafe.Pointer(pszClassList)), + 0) + + return HTHEME(ret) +} + +func SetWindowTheme(hwnd HWND, pszSubAppName, pszSubIdList *uint16) HRESULT { + ret, _, _ := syscall.Syscall(setWindowTheme, 3, + uintptr(hwnd), + uintptr(unsafe.Pointer(pszSubAppName)), + uintptr(unsafe.Pointer(pszSubIdList))) + + return HRESULT(ret) +} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/vars.go b/v2/internal/frontend/desktop/windows/winc/w32/vars.go new file mode 100644 index 000000000..53bc6cb8b --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/w32/vars.go @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2019 Tad Vizbaras. All Rights Reserved. + * Copyright (C) 2010-2012 The W32 Authors. All Rights Reserved. + */ + +package w32 + +var ( + IID_NULL = &GUID{0x00000000, 0x0000, 0x0000, [8]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}} + IID_IUnknown = &GUID{0x00000000, 0x0000, 0x0000, [8]byte{0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}} + IID_IDispatch = &GUID{0x00020400, 0x0000, 0x0000, [8]byte{0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}} + IID_IConnectionPointContainer = &GUID{0xB196B284, 0xBAB4, 0x101A, [8]byte{0xB6, 0x9C, 0x00, 0xAA, 0x00, 0x34, 0x1D, 0x07}} + IID_IConnectionPoint = &GUID{0xB196B286, 0xBAB4, 0x101A, [8]byte{0xB6, 0x9C, 0x00, 0xAA, 0x00, 0x34, 0x1D, 0x07}} +) diff --git a/v2/internal/frontend/desktop/windows/winc/wndproc.go b/v2/internal/frontend/desktop/windows/winc/wndproc.go new file mode 100644 index 000000000..24490d74b --- /dev/null +++ b/v2/internal/frontend/desktop/windows/winc/wndproc.go @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2019 The Winc Authors. All Rights Reserved. + * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. + */ + +package winc + +import ( + "unsafe" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" +) + +var wmInvokeCallback uint32 + +func init() { + wmInvokeCallback = RegisterWindowMessage("WincV0.InvokeCallback") +} + +func genPoint(p uintptr) (x, y int) { + x = int(w32.LOWORD(uint32(p))) + y = int(w32.HIWORD(uint32(p))) + return +} + +func genMouseEventArg(wparam, lparam uintptr) *MouseEventData { + var data MouseEventData + data.Button = int(wparam) + data.X, data.Y = genPoint(lparam) + + return &data +} + +func genDropFilesEventArg(wparam uintptr) *DropFilesEventData { + hDrop := w32.HDROP(wparam) + + var data DropFilesEventData + _, fileCount := w32.DragQueryFile(hDrop, 0xFFFFFFFF) + data.Files = make([]string, fileCount) + + var i uint + for i = 0; i < fileCount; i++ { + data.Files[i], _ = w32.DragQueryFile(hDrop, i) + } + + data.X, data.Y, _ = w32.DragQueryPoint(hDrop) + w32.DragFinish(hDrop) + return &data +} + +func generalWndProc(hwnd w32.HWND, msg uint32, wparam, lparam uintptr) uintptr { + + switch msg { + case w32.WM_HSCROLL: + //println("case w32.WM_HSCROLL") + + case w32.WM_VSCROLL: + //println("case w32.WM_VSCROLL") + } + + if controller := GetMsgHandler(hwnd); controller != nil { + ret := controller.WndProc(msg, wparam, lparam) + + switch msg { + case w32.WM_NOTIFY: //Reflect notification to control + nm := (*w32.NMHDR)(unsafe.Pointer(lparam)) + if controller := GetMsgHandler(nm.HwndFrom); controller != nil { + ret := controller.WndProc(msg, wparam, lparam) + if ret != 0 { + w32.SetWindowLong(hwnd, w32.DWL_MSGRESULT, uint32(ret)) + return w32.TRUE + } + } + case w32.WM_COMMAND: + if lparam != 0 { //Reflect message to control + h := w32.HWND(lparam) + if controller := GetMsgHandler(h); controller != nil { + ret := controller.WndProc(msg, wparam, lparam) + if ret != 0 { + w32.SetWindowLong(hwnd, w32.DWL_MSGRESULT, uint32(ret)) + return w32.TRUE + } + } + } + case w32.WM_CLOSE: + controller.OnClose().Fire(NewEvent(controller, nil)) + case w32.WM_KILLFOCUS: + controller.OnKillFocus().Fire(NewEvent(controller, nil)) + case w32.WM_SETFOCUS: + controller.OnSetFocus().Fire(NewEvent(controller, nil)) + case w32.WM_DROPFILES: + controller.OnDropFiles().Fire(NewEvent(controller, genDropFilesEventArg(wparam))) + case w32.WM_CONTEXTMENU: + if wparam != 0 { //Reflect message to control + h := w32.HWND(wparam) + if controller := GetMsgHandler(h); controller != nil { + contextMenu := controller.ContextMenu() + x, y := genPoint(lparam) + + if contextMenu != nil { + id := w32.TrackPopupMenuEx( + contextMenu.hMenu, + w32.TPM_NOANIMATION|w32.TPM_RETURNCMD, + int32(x), + int32(y), + controller.Handle(), + nil) + + item := findMenuItemByID(int(id)) + if item != nil { + item.OnClick().Fire(NewEvent(controller, genMouseEventArg(wparam, lparam))) + } + return 0 + } + } + } + + case w32.WM_LBUTTONDOWN: + controller.OnLBDown().Fire(NewEvent(controller, genMouseEventArg(wparam, lparam))) + case w32.WM_LBUTTONUP: + controller.OnLBUp().Fire(NewEvent(controller, genMouseEventArg(wparam, lparam))) + case w32.WM_LBUTTONDBLCLK: + controller.OnLBDbl().Fire(NewEvent(controller, genMouseEventArg(wparam, lparam))) + case w32.WM_MBUTTONDOWN: + controller.OnMBDown().Fire(NewEvent(controller, genMouseEventArg(wparam, lparam))) + case w32.WM_MBUTTONUP: + controller.OnMBUp().Fire(NewEvent(controller, genMouseEventArg(wparam, lparam))) + case w32.WM_RBUTTONDOWN: + controller.OnRBDown().Fire(NewEvent(controller, genMouseEventArg(wparam, lparam))) + case w32.WM_RBUTTONUP: + controller.OnRBUp().Fire(NewEvent(controller, genMouseEventArg(wparam, lparam))) + case w32.WM_RBUTTONDBLCLK: + controller.OnRBDbl().Fire(NewEvent(controller, genMouseEventArg(wparam, lparam))) + case w32.WM_MOUSEMOVE: + controller.OnMouseMove().Fire(NewEvent(controller, genMouseEventArg(wparam, lparam))) + case w32.WM_PAINT: + canvas := NewCanvasFromHwnd(hwnd) + defer canvas.Dispose() + controller.OnPaint().Fire(NewEvent(controller, &PaintEventData{Canvas: canvas})) + case w32.WM_KEYUP: + controller.OnKeyUp().Fire(NewEvent(controller, &KeyUpEventData{int(wparam), int(lparam)})) + case w32.WM_SIZE: + x, y := genPoint(lparam) + controller.OnSize().Fire(NewEvent(controller, &SizeEventData{uint(wparam), x, y})) + case wmInvokeCallback: + controller.invokeCallbacks() + } + return ret + } + + return w32.DefWindowProc(hwnd, uint32(msg), wparam, lparam) +} diff --git a/v2/internal/frontend/desktop/windows/window.go b/v2/internal/frontend/desktop/windows/window.go index 3d7b3c244..985a509a7 100644 --- a/v2/internal/frontend/desktop/windows/window.go +++ b/v2/internal/frontend/desktop/windows/window.go @@ -7,8 +7,8 @@ import ( "github.com/wailsapp/wails/v2/internal/system/operatingsystem" "unsafe" - "github.com/leaanthony/winc" - "github.com/leaanthony/winc/w32" + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc" + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" "github.com/wailsapp/wails/v2/pkg/menu" "github.com/wailsapp/wails/v2/pkg/options" ) diff --git a/v2/internal/gomod/gomod_test.go b/v2/internal/gomod/gomod_test.go index 01b001abf..a901da0e1 100644 --- a/v2/internal/gomod/gomod_test.go +++ b/v2/internal/gomod/gomod_test.go @@ -29,11 +29,11 @@ require ( github.com/leaanthony/debme v1.2.1 // indirect github.com/leaanthony/go-ansi-parser v1.0.1 // indirect github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect - github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect github.com/leaanthony/gosod v1.0.3 // indirect github.com/leaanthony/slicer v1.5.0 // indirect github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect - github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect @@ -92,11 +92,11 @@ require ( github.com/leaanthony/debme v1.2.1 // indirect github.com/leaanthony/go-ansi-parser v1.0.1 // indirect github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect - github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect github.com/leaanthony/gosod v1.0.3 // indirect github.com/leaanthony/slicer v1.5.0 // indirect github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect - github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect @@ -133,11 +133,11 @@ require ( github.com/leaanthony/debme v1.2.1 // indirect github.com/leaanthony/go-ansi-parser v1.0.1 // indirect github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect - github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect github.com/leaanthony/gosod v1.0.3 // indirect github.com/leaanthony/slicer v1.5.0 // indirect github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect - github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect @@ -173,11 +173,11 @@ require ( github.com/leaanthony/debme v1.2.1 // indirect github.com/leaanthony/go-ansi-parser v1.0.1 // indirect github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect - github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect github.com/leaanthony/gosod v1.0.3 // indirect github.com/leaanthony/slicer v1.5.0 // indirect github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect - github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect @@ -214,11 +214,11 @@ require ( github.com/leaanthony/debme v1.2.1 // indirect github.com/leaanthony/go-ansi-parser v1.0.1 // indirect github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect - github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect github.com/leaanthony/gosod v1.0.3 // indirect github.com/leaanthony/slicer v1.5.0 // indirect github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect - github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect @@ -255,11 +255,11 @@ require ( github.com/leaanthony/debme v1.2.1 // indirect github.com/leaanthony/go-ansi-parser v1.0.1 // indirect github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect - github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect github.com/leaanthony/gosod v1.0.3 // indirect github.com/leaanthony/slicer v1.5.0 // indirect github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect - github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect @@ -298,11 +298,11 @@ require ( github.com/leaanthony/debme v1.2.1 // indirect github.com/leaanthony/go-ansi-parser v1.0.1 // indirect github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect - github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect github.com/leaanthony/gosod v1.0.3 // indirect github.com/leaanthony/slicer v1.5.0 // indirect github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect - github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect @@ -342,11 +342,11 @@ require ( github.com/leaanthony/debme v1.2.1 // indirect github.com/leaanthony/go-ansi-parser v1.0.1 // indirect github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect - github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect github.com/leaanthony/gosod v1.0.3 // indirect github.com/leaanthony/slicer v1.5.0 // indirect github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect - github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect @@ -444,11 +444,11 @@ require ( github.com/leaanthony/debme v1.2.1 // indirect github.com/leaanthony/go-ansi-parser v1.0.1 // indirect github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect - github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect github.com/leaanthony/gosod v1.0.3 // indirect github.com/leaanthony/slicer v1.5.0 // indirect github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect - github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect @@ -485,11 +485,11 @@ require ( github.com/leaanthony/debme v1.2.1 // indirect github.com/leaanthony/go-ansi-parser v1.0.1 // indirect github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect - github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect github.com/leaanthony/gosod v1.0.3 // indirect github.com/leaanthony/slicer v1.5.0 // indirect github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect - github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect @@ -526,11 +526,11 @@ require ( github.com/leaanthony/debme v1.2.1 // indirect github.com/leaanthony/go-ansi-parser v1.0.1 // indirect github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect - github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect github.com/leaanthony/gosod v1.0.3 // indirect github.com/leaanthony/slicer v1.5.0 // indirect github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect - github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect @@ -570,11 +570,11 @@ require ( github.com/leaanthony/debme v1.2.1 // indirect github.com/leaanthony/go-ansi-parser v1.0.1 // indirect github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect - github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect github.com/leaanthony/gosod v1.0.3 // indirect github.com/leaanthony/slicer v1.5.0 // indirect github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect - github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc v0.0.0-20210921073452-54963136bf18 // indirect github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect diff --git a/v2/internal/system/system_windows.go b/v2/internal/system/system_windows.go index ce8c90028..4bb6d6707 100644 --- a/v2/internal/system/system_windows.go +++ b/v2/internal/system/system_windows.go @@ -4,7 +4,7 @@ package system import ( - "github.com/leaanthony/go-webview2/webviewloader" + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/webviewloader" "github.com/wailsapp/wails/v2/internal/system/operatingsystem" "github.com/wailsapp/wails/v2/internal/system/packagemanager" ) diff --git a/v2/pkg/commands/build/base.go b/v2/pkg/commands/build/base.go index 6e7b9caf5..6c5b09ffa 100644 --- a/v2/pkg/commands/build/base.go +++ b/v2/pkg/commands/build/base.go @@ -13,7 +13,6 @@ import ( "github.com/wailsapp/wails/v2/internal/system" "github.com/leaanthony/gosod" - wailsRuntime "github.com/wailsapp/wails/v2/internal/frontend/runtime" "github.com/wailsapp/wails/v2/internal/frontend/runtime/wrapper" "github.com/pkg/errors" @@ -436,16 +435,6 @@ func generateRuntimeWrapper(options *Options) error { return err } - //ipcdev.js - err = os.WriteFile(filepath.Join(wrapperDir, "ipcdev.js"), wailsRuntime.DesktopIPC, 0755) - if err != nil { - return err - } - //runtimedev.js - err = os.WriteFile(filepath.Join(wrapperDir, "runtimedev.js"), wailsRuntime.RuntimeDesktopJS, 0755) - if err != nil { - return err - } return nil } diff --git a/website/docs/reference/cli.mdx b/website/docs/reference/cli.mdx index 13b0bae00..30a6466c3 100644 --- a/website/docs/reference/cli.mdx +++ b/website/docs/reference/cli.mdx @@ -173,9 +173,8 @@ Your system is ready for Wails development! | -devserverurl "url" | Use 3rd party dev server url, EG Vite | "http://localhost:34115" | | -appargs "args" | Arguments passed to the application in shell style | | | -platform "platform" | Platform/Arch to target | `runtime.GOOS` | - -If the `assetdir`, `reloaddirs`, `wailsjsdir`, `debounce` or `devserverurl` flags are provided on the command line, they are saved in -`wails.json`, and become the defaults for subsequent invocations. +| -save | Saves the given `assetdir`, `reloaddirs`, `wailsjsdir`, `debounce` and `devserverurl` flags in + `wails.json` to become the defaults for subsequent invocations. | | Example: