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 89ce03a5..7cc39114 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 86e6a1fd..3c219b52 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 875bab06..d3dd6ecd 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 6c0aac70..06f70133 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 eb6e2e88..bba38583 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 6c0aac70..06f70133 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 eb6e2e88..bba38583 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 14e1b5a3..ce475f5b 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 ee11171e..f408167d 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 563dff79..6f94e211 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 59174e83..cf70cef9 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 7069ccaa..f47b25bb 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 f98daf96..1d0a1b18 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 108386c1..7b9f9592 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 e8ddf4ab..d8d6a3ad 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 00000000..ef2a0f48 --- /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 00000000..5555fa61 --- /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 00000000..cfd1bfbd --- /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 00000000..43eb12e0 --- /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 00000000..6a378fcb --- /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 00000000..0b571588 --- /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 00000000..e901101e --- /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 00000000..8fc93e0e --- /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 00000000..9ca9c41c --- /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 00000000..e3ff7bb5 --- /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 00000000..093cc804 --- /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 00000000..8ecdd57e --- /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 00000000..26835495 --- /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 00000000..ca92d34b --- /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 00000000..e304bb9d --- /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 00000000..6fbfe724 --- /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 00000000..7f6e1b9b --- /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 00000000..06a9671c --- /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 00000000..9b093bd2 --- /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 00000000..8cdf58e8 --- /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 00000000..a6a0edda --- /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 00000000..c2441ab2 --- /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 00000000..9802b8c0 --- /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 00000000..701601bc --- /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 00000000..75ed3e7b --- /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 00000000..ef901092 --- /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 00000000..8362f4bb --- /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 00000000..9835e3ec --- /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 00000000..92b2e297 --- /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 00000000..9c23da2f --- /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 00000000..64d89534 --- /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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

<5wpmu$IPOu%o!P`BnUE?KM;oGeVDi* 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 literal 0 HcmV?d00001 diff --git a/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/module.go b/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/module.go new file mode 100644 index 00000000..7566c289 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/module.go @@ -0,0 +1,140 @@ +package webviewloader + +import ( + "fmt" + "sync" + "unsafe" + + "github.com/jchv/go-winloader" + "golang.org/x/sys/windows" +) + +var ( + nativeModule = windows.NewLazyDLL("WebView2Loader") + nativeCreate = nativeModule.NewProc("CreateCoreWebView2EnvironmentWithOptions") + nativeCompareBrowserVersions = nativeModule.NewProc("CompareBrowserVersions") + nativeGetAvailableCoreWebView2BrowserVersionString = nativeModule.NewProc("GetAvailableCoreWebView2BrowserVersionString") + + memOnce sync.Once + memModule winloader.Module + memCreate winloader.Proc + memCompareBrowserVersions winloader.Proc + memGetAvailableCoreWebView2BrowserVersionString winloader.Proc + memErr error +) + +// CompareBrowserVersions will compare the 2 given versions and return: +// -1 = v1 < v2 +// 0 = v1 == v2 +// 1 = v1 > 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 00000000..174a0703 --- /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 00000000..53e715fd --- /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 00000000..a09d3fae --- /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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 literal 0 HcmV?d00001 diff --git a/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/x86/WebView2Loader.dll b/v2/internal/frontend/desktop/windows/go-webview2/webviewloader/x86/WebView2Loader.dll new file mode 100644 index 0000000000000000000000000000000000000000..8609d58ee5655f32398d81cade8da650df7025eb GIT binary patch literal 107416 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

V%m1za#if-l!kY$L?=l^$~GQQCKQ2z2M>+7IR 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[ZLoT@Yu?$v2Y%f6uD?u*=g+32Wwssmr<{KF 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 UH>mB@v=66Z+LA>Dmu*S~0BKM|o&W#< literal 0 HcmV?d00001 diff --git a/v2/internal/frontend/desktop/windows/keys.go b/v2/internal/frontend/desktop/windows/keys.go index bec6eea4..2fe5f755 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 d1a34fd1..b71128b4 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 00000000..f1c181ec --- /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 00000000..85674b8d --- /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 00000000..a063a437 --- /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 00000000..4d4d467b --- /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 00000000..a2c3237c --- /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 00000000..235dc43f --- /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 00000000..55e8ec52 --- /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 00000000..ccf0d267 --- /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 00000000..46fc0d61 --- /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 00000000..ac16dbc4 --- /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 00000000..b389c8b4 --- /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 00000000..3e30f14b --- /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 00000000..1f7cb66f --- /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 00000000..4fd03881 --- /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 00000000..721acbf7 --- /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 zcmeAS@N?(olHy`uVBq!ia0y~yU_8XYz{tkI#=yYvEph5|1_lPk;vjb?hIQv;UNSH+ zu%tWsIx;Y9?C1WI$jZRLz**oCS4R`LM~{E%jqpD$NiLl{`||ZPqEZPPHvyuzuFUvca+xE+1-F2FN8LVN=^}r7`3qq?JI51T z(ygH%`JY~!Udp-t^y9ZzW)*Mjz9pW${P^svS(zdso4;podGqwzZ-d+FYwR6QoXRd| zf12;CIsLNVtGC-OeJlJDm11n07rt89|I*1HcD%bRH{Duky<%It`p&QGX8mgUs?HIq z@9%fY;ElY@RjoNUPZcivwbRD$_SRP`Hgagoecie(IC$T*skfi}_+@AQ)%sV>=LPeN zvZm*TH&@5~ZrW&avu4h%==f#5S;hM*pX=Z1`gm3Q^t0-s-FAsbMfdj`{4dOH^v^eV zv+c4$c+1sqH(qX-T<>PR{OGTY`6jnCcz3=1V|?diwW!-~zw;Bf-@aRO<;9Lq8!r3Z z+i$z)ns4F-*=_32cWFKU__gkI@%3%lZ#S>ot}k_3cgl5J>t(Lb*Wb!MUR&^gL({BH z>rOa`D!zXEssD@j{>gs+%lQA9eT!e>x69*wx7V{b+pnCC*8g^V>*>iQpVI21PVPB> z_0c=mSA6%DoYcGi9U)W~&4@!xK(thTtE|9|=y_h8noJ+8?+SF4`CZ#nl~clBHO zuqg^DdwK6It=no9y65z(<>k#;oBvwWUs_XMaG{{)?6JBk;}^@i?_OMdXm!8E@0SV1 ze>gTqlohAkJ7|==vzy=UzwK9kZ7+dMb*r@A-(Ivq^ImAL-CD^d<$rIzIreY&AJ5I_ zUK%`|J)X|#S*Ov22KcaE~pzwZ+$XXbdK zN>gp$n{Pq8t1V`+?)+$CoBjP#UM}02ryoyW$+q|Q?{=vAl-svDuF7)dlXI8uo%w#> z>|6ZuJS&gc-1|=j%r=euYHjg=x&6BK`meoq57o1C z;ti&yn)*%CJY)U;$Gj`w4FlWSlRp*5M|bh$i2mQadGqA$>!oCwj|A4#CK>OoHuTy0 z)8s>f$oo&}PkFv%h&0@Q_e%w3%*CxG(rImZ;MiuLo1^!NY`=w%Ylx}ZR*0;LeX?yQf$*28p zX?GOZbm`Vz$DjSyQ%+~9e=Up7{B2RCyJ@D}_SIKA)nBI6%xAkR6Z!UvY{>1ylkZf8 z`_>*htiTZ|Pm^rQEAszH)W65KnC4$gxRJX&aUI9%w;L}fs{gMk^XvY4(JpgKpj652 zpU<~X{;t*YZSFkf{O!AcuDDqEG;#UdJ@wTUYiA!?V4dTCt~B)Ap zV8O#5vwHu2NxR@HzI(BIYq)&jx622g{SZvr_~zM(J{#Cz{51XNovNt63 z-gei-;61CQYNL!j-v8Qqv-fZL&&g?XFDIU|oX)pz{^!sJC7#oq>dP}z+OB+`yoiVY^po`c+e9Q^h<|2^{P?x= z?3Z^(4X!@Be4sgK&-wXNjV~+?WJrQ!jm8}1xBEXvOql`6tavLo^qhI$or8gY?tOO- z)SRiUemdkegFs&waaAU&sj!+DsZOP%0+uiNzrME5E%~zTR*ZH2<%@o$fAjkoLS@gwmWy}dHdC2 z*{2M4Kb$u;cRSaXXLb)33bw4-vR1y{@A>oRi=V!j{pI&Vzh4ipzu0;*{pIJiSz8ZI zt)4dh@OroT^`$em*Z-jh zb}lV({rDpPdHar8uo-CoD`ea|=6(f>7mn*Ek|NL1pf4e*H-)a4_@=M>>X>BXE z{Cal9-{sos`&QOojCj>m9CH2Bi``!??|8C1{J*RG1}PTB*RSu_{rdc9c1g%@?_U?o z?>}GgNBeK^lDoG*%gnsLwFYgAG4%~UHb$@yF(;}zqse$`5SsmkPs@`)ukJrKY4?s{%5Vz ztkWd9KLm0(U2`}5`1Hf~n2&$nuQ*kC#VxS2VTXTzzn1>0ef|219@EOI`9G?H zPVV(}H;$LD_;R=VOLEoJdHQo)f@Zry>hFKQoeT;m-R;mi=y`1Q@__euV1#l zex1Lardo;2()?97ryu?F=luH1=O=$}h+lem(-Z0Wk4u03Ir`>0`?BXdzHRL(GRwUB z{al99=@+7Lg=fycd~N^w{i9Do+&A6duD^WEmG8!%%FEYnK`k%t4;#0fJAb|%)Cyj# z#G)`Tc@Ae=nvVG%;f5$}{lgcv7>*Ry)y4f=i5@7Xe$yoR&dz6i@GxU**N)w%QY0Bp z5Ls}_Z8%-B=dM?E`qgK*EWNAOzWR0kTTu3Ii~fwcwp%`a2;gwC+ZY%j`kyZWRG#dz zx#zdmQhfJ{+KpvZdm`8RncUsHSH}2s_O5li<6j%iI>tPInnH@|mfg*tbnY+UjP1Bq{ zTCCeTch;w^YueO~U;n;y<^7A#XS4sE61+QSx73}_x5Rsvo_zNB%DrDkxgoQ|bC)bX zUvwyD&-rw@-DgYZ*FM-PmV0!slCWuiTfaqKzb)RID!KXln#$@pdEe``zq8lgTA$nhzkc<%^>_7u zgrpU(i#T`w{ApL$6R&pvQT}`B{gTU*)vvPeWV=;A(dwnrUj0?Qo9_qd=kL8Ce_h=F z(#hF>Gq--U3-8`7zS6p4vfA#L%ijCXPD|Z+s^(_)@^=q?_WzB)B<~w~&InZC{9>OS zdUfkezF%*@yva1(@%vTs$5$+iQGe8LM?0d9o&6o zec1A@@4kP(&0&*=b5-{_r}X6;KpBYM#yNA2{U zQ$>xb$37{}tC_ui3)hO@uO27IYOj%SJn;(L*(f?%Y*F=O+eZ`I*)v5eEB_dMy>&k5 zr^yDMMtC=){0h9Aksfv}%`EJ%-}&lh?a3ihphD`Y^VI(j16Z$Ysx^uIUSGI&L({sw z4lV!Bl{6hK$((h?XqJHS&Ga%RH_L~q-+o-&xxCmGsSP6mZNt3X(|YV1YjWg@m-DV3 zkBsZ_1(c|e0FHQf19hubnv+0Psw$U+=Em z?4KI;_uu#MMW*{hWiFmCll|Qi@6WN*@z+MJ|Hd9?Q==Yzt<1KIdcNnT=WF%s<8yER zN?R(w>f-s;``3tMuRrv{?%{XkmOB=2&*pu-ziaKZy-#n%W*z+#_HE;doI9D<&Dq70 zpZ_R+l_z#G>v-DsefMu`UW>c7qxR5ycaEJm#mZMnlwC8EQl4{h;^Px9o_`KIWA5gj z%%1pk<-Th*J+HG(i=_AL`#ERxx7+nKWow09x2`Td{Ln7{Z%eXd`ZvEV&;JPkreoWaZf+*a@UZ@Klr`s;6Vw=E9u;duS~__wOlN9Uh>y7yDr zf6I&egJ0%t>$cx~`sik_zt898EZ%Ydo=)7=`q;0V_C9^|^X0jz_hZrbUb|l08Btwx&Y$s= zM3}JOx1_w>ZH2uXC1Pd${Umjs_v)zcII%J27=QkKpTHtPr@U=Nk4|68+hTtG_hyCk ze_icw??z;uY52M4t4-N${>ST$Ji6<}ci!rK-aPS6-I|_1|8mp5rsf=rI^*|8(>eKC z%HI2v-_CpZ;_}T#T~koNp5FD~TlsW9wG$xkM$OPUe74Z0a`Dcd*;g*j7cj2yOFrba zbe4tDuV2SfW}bSnWLA{v#AO@b7EX?y^?LU2yf@#!w9G$r^n8z|ID%D>3cKIEN{FYR+(ELa?xSA z8{-LI4n_}O4#l-IBWz2eL|OzIu8@My{rSzo_@Mmxdu0}soVYb>lTWQQ!zIJ)DcPat zZZPfe^Xp}rFiQY|Z_QME^R-Is^i(|t^NE6r3@bTDF_pu>;8FCR%1r{&@~?IWKX=a2 zx$^%1VK*I{%3zs`-+yn|D8SvzpZMe7)i=$v9XS%Uz9w%CsF5_Ep4oYQySaaxl5y6h zUnieE`X>MNY@1eEdQIBZUGKSW{irplU1rlVKRJ7!sP^x#cUNwf*IxVU@B8&G*>$Te z7M*@oueLU6cY6N&^f%LA8+`u~DkCHFWqR-Lk7c!r^Y)yyGyAEWcmAsRafx4Nx>En< z{r|Z8_0hlL{Ex!^zhim+zWL=RZl@bRlJaye3#~sN{rRHR^Rs?Y_L0`_&RjER*Ax8l zw>RRo&hfbQ;(HHYouMfw^JI}q%5zd z|6g63&cfQYZh6hu&F2?itNYz=f6K0G(XFey9u~~sUUz6?&*$TBXZ-%$&wVuaOUR4X z+T(URX4c*8*s%9zZ`{wPF?!XfH}1aD^>gi;#}jVuxV$buZt1tVNvEIx^!)ww*WK8T zq!$Lu5XT%j`EIwODj%wR~Y>A zjd%I-^V|H5f%(55Ja}R$Z+7g>cB{7)+H!j|-isez`TOrz{Wi0jYri~?Ukb@vT&KTX z?nw7z#rso#iT-{NzH?si;%@6u9_wuBLL)y(S<7`M*F^2|kN#OAm|8h&y{Y`;Us1hX zZ0qywp0D})O@4mtYnQ;cS$7L|oR7AD7;$XoUg5Vp|A#2=_&j&-+gB3P%NGAgNZuB+ zeUs@!2`=$_`oYhm)$hsQ?e9G{{mWYR{_cRf&h@Ep?PNqw?vd|jSKKB2`fPA>eD}pi zk%BT7eRm9ta_t)MtYmPlUm-PQ( z@tnVPr+sC@{x=2k>|Y$c?tY~0W81p_bu*pf|MxEb^{n&Lt!sgQesA4pUB0^_WzY88 zx0B=U2lTbX+4kzc{=0%_{v(^Nx9-O`YEJ(Otwt6+%x-z@`tRhsG@ofd|LDa{zrIx} z{)F-B^IZXQN0)z+uKPbVL-J>p#A)FueWo#r;PUKfcSKvRqzy z+x+>)lQ;DwicabMu~{N>ZcLK4TYmEm8GUGlbbx=Cq~e>p9>3bpE|s#Yh2?jDy<5Dl z`t7>4y_L1jmakrdtHqK>A6_2xTXWXT-&9ZZDrO$qO9%q_fP^-Acy|R9K^XyMnq5m(o<<-0~Ni!?66YKBUz9)R% zziv?ddY<8wMA&lS@6h^nUZUleIp&+@JWx7bq#6FFZf2JA_1$uN`>WT_n(TjyQ&zfg z|Ge5rz5aXmyThAbHPqsd{y-{C zy}ciQozLEv_F`|ZM4kNdxpDR{BlH&ilzZK-HTiyi>Dn(p=PsPR^Y( zOEU7T+S>1TU%9W6pYh4`ZpDvXAMc%7`jPqb%>8n1yXUo5X6 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 00000000..b146dbf2 --- /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 00000000..c3da17a6 --- /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 00000000..97bc79b2 --- /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 00000000..45bf32f8 --- /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 00000000..acd4856f --- /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 00000000..be97a443 --- /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 00000000..8ec9e80c --- /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 00000000..ea38fd98 --- /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 00000000..b866ea7b --- /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 00000000..b16f041c --- /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 00000000..a138b55f --- /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 00000000..bbfdefad --- /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 00000000..12cf634c --- /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 00000000..a7b61631 --- /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 00000000..4bdf92b9 --- /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 00000000..99f1829e --- /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 00000000..c8091186 --- /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 00000000..19f3c51f --- /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 00000000..43daa753 --- /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 00000000..9c0ac1f7 --- /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 00000000..395dfed6 --- /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 00000000..2a1e5503 --- /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 00000000..16187711 --- /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 00000000..ae1fe393 --- /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 00000000..f18ba1cd --- /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 00000000..4976fa65 --- /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 00000000..aa4304a1 --- /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 00000000..0f37403a --- /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 00000000..936bcf33 --- /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 00000000..c8191ad5 --- /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 00000000..56545a94 --- /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 00000000..f54c0b53 --- /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 00000000..a8d7daff --- /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 00000000..2396c9c1 --- /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 00000000..a937c7ac --- /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 00000000..388cf10a --- /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 00000000..66a9b5f6 --- /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 00000000..d5cc0df4 --- /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 00000000..35cc8833 --- /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 00000000..1d23ac1f --- /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 00000000..4584a959 --- /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 00000000..eb7aaeea --- /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 00000000..9c0c9ffe --- /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 00000000..c9f38161 --- /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 00000000..a6a744d1 --- /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 00000000..cb8d354d --- /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 00000000..67b02872 --- /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 00000000..53bc6cb8 --- /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 00000000..24490d74 --- /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 3d7b3c24..985a509a 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 01b001ab..a901da0e 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 ce8c9002..4bb6d670 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 6e7b9caf..6c5b09ff 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 13b0bae0..30a6466c 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: