diff --git a/v2/cmd/wails/internal/dev/dev.go b/v2/cmd/wails/internal/dev/dev.go index ca40f36a4..b7fc4d10d 100644 --- a/v2/cmd/wails/internal/dev/dev.go +++ b/v2/cmd/wails/internal/dev/dev.go @@ -22,6 +22,7 @@ import ( "github.com/wailsapp/wails/v2/cmd/wails/flags" "github.com/wailsapp/wails/v2/cmd/wails/internal/gomod" "github.com/wailsapp/wails/v2/cmd/wails/internal/logutils" + "golang.org/x/mod/semver" "github.com/wailsapp/wails/v2/pkg/commands/buildtags" @@ -36,6 +37,10 @@ import ( "github.com/wailsapp/wails/v2/pkg/commands/build" ) +const ( + viteMinVersion = "v3.0.0" +) + func sliceToMap(input []string) map[string]struct{} { result := map[string]struct{}{} for _, value := range input { @@ -88,10 +93,11 @@ func Application(f *flags.Dev, logger *clilogger.CLILogger) error { buildOptions.IgnoreApplication = false } + legacyUseDevServerInsteadofCustomScheme := false // frontend:dev:watcher command. frontendDevAutoDiscovery := projectConfig.IsFrontendDevServerURLAutoDiscovery() if command := projectConfig.DevWatcherCommand; command != "" { - closer, devServerURL, err := runFrontendDevWatcherCommand(projectConfig.GetFrontendDir(), command, frontendDevAutoDiscovery) + closer, devServerURL, devServerViteVersion, err := runFrontendDevWatcherCommand(projectConfig.GetFrontendDir(), command, frontendDevAutoDiscovery) if err != nil { return err } @@ -100,6 +106,12 @@ func Application(f *flags.Dev, logger *clilogger.CLILogger) error { f.FrontendDevServerURL = devServerURL } defer closer() + + if devServerViteVersion != "" && semver.Compare(devServerViteVersion, viteMinVersion) < 0 { + logutils.LogRed("Please upgrade your Vite Server to at least '%s' future Wails versions will require at least Vite '%s'", viteMinVersion, viteMinVersion) + time.Sleep(3 * time.Second) + legacyUseDevServerInsteadofCustomScheme = true + } } else if frontendDevAutoDiscovery { return fmt.Errorf("unable to auto discover frontend:dev:serverUrl without a frontend:dev:watcher command, please either set frontend:dev:watcher or remove the auto discovery from frontend:dev:serverUrl") } @@ -107,7 +119,7 @@ func Application(f *flags.Dev, logger *clilogger.CLILogger) error { // Do initial build but only for the application. logger.Println("Building application for development...") buildOptions.IgnoreFrontend = true - debugBinaryProcess, appBinary, err := restartApp(buildOptions, nil, f, exitCodeChannel) + debugBinaryProcess, appBinary, err := restartApp(buildOptions, nil, f, exitCodeChannel, legacyUseDevServerInsteadofCustomScheme) buildOptions.IgnoreFrontend = ignoreFrontend || f.FrontendDevServerURL != "" if err != nil { return err @@ -153,7 +165,7 @@ func Application(f *flags.Dev, logger *clilogger.CLILogger) error { }() // Watch for changes and trigger restartApp() - debugBinaryProcess = doWatcherLoop(buildOptions, debugBinaryProcess, f, watcher, exitCodeChannel, quitChannel, f.DevServerURL()) + debugBinaryProcess = doWatcherLoop(buildOptions, debugBinaryProcess, f, watcher, exitCodeChannel, quitChannel, f.DevServerURL(), legacyUseDevServerInsteadofCustomScheme) // Kill the current program if running and remove dev binary if err := killProcessAndCleanupBinary(debugBinaryProcess, appBinary); err != nil { @@ -202,7 +214,7 @@ func runCommand(dir string, exitOnError bool, command string, args ...string) er } // runFrontendDevWatcherCommand will run the `frontend:dev:watcher` command if it was given, ex- `npm run dev` -func runFrontendDevWatcherCommand(frontendDirectory string, devCommand string, discoverViteServerURL bool) (func(), string, error) { +func runFrontendDevWatcherCommand(frontendDirectory string, devCommand string, discoverViteServerURL bool) (func(), string, string, error) { ctx, cancel := context.WithCancel(context.Background()) scanner := NewStdoutScanner() cmdSlice := strings.Split(devCommand, " ") @@ -214,7 +226,7 @@ func runFrontendDevWatcherCommand(frontendDirectory string, devCommand string, d if err := cmd.Start(); err != nil { cancel() - return nil, "", fmt.Errorf("unable to start frontend DevWatcher: %w", err) + return nil, "", "", fmt.Errorf("unable to start frontend DevWatcher: %w", err) } var viteServerURL string @@ -224,10 +236,19 @@ func runFrontendDevWatcherCommand(frontendDirectory string, devCommand string, d viteServerURL = serverURL case <-time.After(time.Second * 10): cancel() - return nil, "", errors.New("failed to find Vite server URL") + return nil, "", "", errors.New("failed to find Vite server URL") } } + viteVersion := "" + select { + case version := <-scanner.ViteServerVersionC: + viteVersion = version + + case <-time.After(time.Second * 5): + // That's fine, then most probably it was not vite that was running + } + logutils.LogGreen("Running frontend DevWatcher command: '%s'", devCommand) var wg sync.WaitGroup wg.Add(1) @@ -255,11 +276,11 @@ func runFrontendDevWatcherCommand(frontendDirectory string, devCommand string, d } cancel() wg.Wait() - }, viteServerURL, nil + }, viteServerURL, viteVersion, nil } // restartApp does the actual rebuilding of the application when files change -func restartApp(buildOptions *build.Options, debugBinaryProcess *process.Process, f *flags.Dev, exitCodeChannel chan int) (*process.Process, string, error) { +func restartApp(buildOptions *build.Options, debugBinaryProcess *process.Process, f *flags.Dev, exitCodeChannel chan int, legacyUseDevServerInsteadofCustomScheme bool) (*process.Process, string, error) { appBinary, err := build.Build(buildOptions) println() @@ -297,6 +318,9 @@ func restartApp(buildOptions *build.Options, debugBinaryProcess *process.Process os.Setenv("assetdir", f.AssetDir) os.Setenv("devserver", f.DevServer) os.Setenv("frontenddevserverurl", f.FrontendDevServerURL) + if legacyUseDevServerInsteadofCustomScheme { + os.Setenv("legacyusedevsererinsteadofcustomscheme", "true") + } // Start up new binary with correct args newProcess := process.NewProcess(appBinary, args...) @@ -316,7 +340,7 @@ func restartApp(buildOptions *build.Options, debugBinaryProcess *process.Process } // doWatcherLoop is the main watch loop that runs while dev is active -func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Process, f *flags.Dev, watcher *fsnotify.Watcher, exitCodeChannel chan int, quitChannel chan os.Signal, devServerURL *url.URL) *process.Process { +func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Process, f *flags.Dev, watcher *fsnotify.Watcher, exitCodeChannel chan int, quitChannel chan os.Signal, devServerURL *url.URL, legacyUseDevServerInsteadofCustomScheme bool) *process.Process { // Main Loop var extensionsThatTriggerARebuild = sliceToMap(strings.Split(f.Extensions, ",")) var dirsThatTriggerAReload []string @@ -422,7 +446,7 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc rebuild = false logutils.LogGreen("[Rebuild triggered] files updated") // Try and build the app - newBinaryProcess, _, err := restartApp(buildOptions, debugBinaryProcess, f, exitCodeChannel) + newBinaryProcess, _, err := restartApp(buildOptions, debugBinaryProcess, f, exitCodeChannel, legacyUseDevServerInsteadofCustomScheme) if err != nil { logutils.LogRed("Error during build: %s", err.Error()) continue diff --git a/v2/cmd/wails/internal/dev/stdout_scanner.go b/v2/cmd/wails/internal/dev/stdout_scanner.go index d84e4785e..dad4e72cf 100644 --- a/v2/cmd/wails/internal/dev/stdout_scanner.go +++ b/v2/cmd/wails/internal/dev/stdout_scanner.go @@ -2,30 +2,47 @@ package dev import ( "bufio" + "fmt" "net/url" "os" "strings" "github.com/acarl005/stripansi" "github.com/wailsapp/wails/v2/cmd/wails/internal/logutils" + "golang.org/x/mod/semver" ) // stdoutScanner acts as a stdout target that will scan the incoming // data to find out the vite server url type stdoutScanner struct { - ViteServerURLChan chan string + ViteServerURLChan chan string + ViteServerVersionC chan string + versionDetected bool } // NewStdoutScanner creates a new stdoutScanner func NewStdoutScanner() *stdoutScanner { return &stdoutScanner{ - ViteServerURLChan: make(chan string, 2), + ViteServerURLChan: make(chan string, 2), + ViteServerVersionC: make(chan string, 2), } } // Write bytes to the scanner. Will copy the bytes to stdout func (s *stdoutScanner) Write(data []byte) (n int, err error) { input := stripansi.Strip(string(data)) + if !s.versionDetected { + v, err := detectViteVersion(input) + if v != "" || err != nil { + if err != nil { + logutils.LogRed("ViteStdoutScanner: %s", err) + v = "v0.0.0" + } + s.ViteServerVersionC <- v + s.versionDetected = true + } + } + match := strings.Index(input, "Local:") if match != -1 { sc := bufio.NewScanner(strings.NewReader(input)) @@ -47,3 +64,21 @@ func (s *stdoutScanner) Write(data []byte) (n int, err error) { } return os.Stdout.Write(data) } + +func detectViteVersion(line string) (string, error) { + s := strings.Split(strings.TrimSpace(line), " ") + if strings.ToLower(s[0]) != "vite" { + return "", nil + } + + if len(line) < 2 { + return "", fmt.Errorf("unable to parse vite version") + } + + v := s[1] + if !semver.IsValid(v) { + return "", fmt.Errorf("%s is not a valid vite version string", v) + } + + return v, nil +} diff --git a/v2/examples/customlayout/myfrontend/package.json b/v2/examples/customlayout/myfrontend/package.json index 4ac881798..a1b6f8e1a 100644 --- a/v2/examples/customlayout/myfrontend/package.json +++ b/v2/examples/customlayout/myfrontend/package.json @@ -8,6 +8,6 @@ "preview": "vite preview" }, "devDependencies": { - "vite": "^2.9.9" + "vite": "^3.0.7" } } \ No newline at end of file diff --git a/v2/internal/app/app_dev.go b/v2/internal/app/app_dev.go index 32c27fa2e..8373d399f 100644 --- a/v2/internal/app/app_dev.go +++ b/v2/internal/app/app_dev.go @@ -8,9 +8,11 @@ import ( "flag" "fmt" iofs "io/fs" + "net" "net/url" "os" "path/filepath" + "time" "github.com/wailsapp/wails/v2/pkg/assetserver" @@ -104,17 +106,35 @@ func CreateApp(appoptions *options.App) (*App, error) { } if frontendDevServerURL != "" { - if devServer == "" { - return nil, fmt.Errorf("Unable to use FrontendDevServerUrl without a DevServer address") + if os.Getenv("legacyusedevsererinsteadofcustomscheme") != "" { + startURL, err := url.Parse("http://" + devServer) + if err != nil { + return nil, err + } + + ctx = context.WithValue(ctx, "starturl", startURL) } - startURL, err := url.Parse("http://" + devServer) + ctx = context.WithValue(ctx, "frontenddevserverurl", frontendDevServerURL) + + externalURL, err := url.Parse(frontendDevServerURL) if err != nil { return nil, err } - ctx = context.WithValue(ctx, "starturl", startURL) - ctx = context.WithValue(ctx, "frontenddevserverurl", frontendDevServerURL) + if externalURL.Host == "" { + return nil, fmt.Errorf("Invalid frontend:dev:serverUrl missing protocol scheme?") + } + + waitCb := func() { myLogger.Debug("Waiting for frontend DevServer '%s' to be ready", externalURL) } + if !checkPortIsOpen(externalURL.Host, time.Minute, waitCb) { + myLogger.Error("Timeout waiting for frontend DevServer") + } + + handler := assetserver.NewExternalAssetsHandler(myLogger, assetConfig, externalURL) + assetConfig.Assets = nil + assetConfig.Handler = handler + assetConfig.Middleware = nil myLogger.Info("Serving assets from frontend DevServer URL: %s", frontendDevServerURL) } else { @@ -246,3 +266,22 @@ func tryInferAssetDirFromFS(assets iofs.FS) (string, error) { return path, nil } + +func checkPortIsOpen(host string, timeout time.Duration, waitCB func()) (ret bool) { + if timeout == 0 { + timeout = time.Minute + } + + deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + conn, _ := net.DialTimeout("tcp", host, 2*time.Second) + if conn != nil { + conn.Close() + return true + } + + waitCB() + time.Sleep(1 * time.Second) + } + return false +} diff --git a/v2/internal/frontend/desktop/darwin/Role.h b/v2/internal/frontend/desktop/darwin/Role.h index 20e670689..6b8877a09 100644 --- a/v2/internal/frontend/desktop/darwin/Role.h +++ b/v2/internal/frontend/desktop/darwin/Role.h @@ -12,5 +12,6 @@ typedef int Role; static const Role AppMenu = 1; static const Role EditMenu = 2; +static const Role WindowMenu = 3; #endif /* Role_h */ diff --git a/v2/internal/frontend/desktop/darwin/WailsMenu.m b/v2/internal/frontend/desktop/darwin/WailsMenu.m index af03ca6b9..66e5dd399 100644 --- a/v2/internal/frontend/desktop/darwin/WailsMenu.m +++ b/v2/internal/frontend/desktop/darwin/WailsMenu.m @@ -68,12 +68,20 @@ appName = [[NSProcessInfo processInfo] processName]; } WailsMenu *appMenu = [[[WailsMenu new] initWithNSTitle:appName] autorelease]; + + if (ctx.aboutTitle != nil) { + [appMenu addItem:[self newMenuItemWithContext :ctx :[@"About " stringByAppendingString:appName] :@selector(About) :nil :0]]; + [appMenu addItem:[NSMenuItem separatorItem]]; + } + + [appMenu addItem:[self newMenuItem:[@"Hide " stringByAppendingString:appName] :@selector(hide:) :@"h" :NSEventModifierFlagCommand]]; + [appMenu addItem:[self newMenuItem:@"Hide Others" :@selector(hideOtherApplications:) :@"h" :(NSEventModifierFlagOption | NSEventModifierFlagCommand)]]; + [appMenu addItem:[self newMenuItem:@"Show All" :@selector(unhideAllApplications:) :@""]]; + [appMenu addItem:[NSMenuItem separatorItem]]; + id quitTitle = [@"Quit " stringByAppendingString:appName]; NSMenuItem* quitMenuItem = [self newMenuItem:quitTitle :@selector(Quit) :@"q" :NSEventModifierFlagCommand]; quitMenuItem.target = ctx; - if (ctx.aboutTitle != nil) { - [appMenu addItem:[self newMenuItemWithContext :ctx :[@"About " stringByAppendingString:appName] :@selector(About) :nil :0]]; - } [appMenu addItem:quitMenuItem]; [self appendSubmenu:appMenu]; break; @@ -100,6 +108,17 @@ [editMenu appendSubmenu:speechMenu]; [self appendSubmenu:editMenu]; + break; + } + case WindowMenu: + { + WailsMenu *windowMenu = [[[WailsMenu new] initWithNSTitle:@"Window"] autorelease]; + [windowMenu addItem:[self newMenuItem:@"Minimize" :@selector(performMiniaturize:) :@"m" :NSEventModifierFlagCommand]]; + [windowMenu addItem:[self newMenuItem:@"Zoom" :@selector(performZoom:) :@""]]; + [windowMenu addItem:[NSMenuItem separatorItem]]; + [windowMenu addItem:[self newMenuItem:@"Full Screen" :@selector(enterFullScreenMode:) :@"f" :(NSEventModifierFlagControl | NSEventModifierFlagCommand)]]; + [self appendSubmenu:windowMenu]; + break; } } diff --git a/v2/internal/frontend/desktop/darwin/frontend.go b/v2/internal/frontend/desktop/darwin/frontend.go index a5517ffcb..5187be2c0 100644 --- a/v2/internal/frontend/desktop/darwin/frontend.go +++ b/v2/internal/frontend/desktop/darwin/frontend.go @@ -113,7 +113,6 @@ func (f *Frontend) startMessageProcessor() { func (f *Frontend) startRequestProcessor() { for request := range requestBuffer { f.assets.ServeWebViewRequest(request) - request.Release() } } func (f *Frontend) startCallbackProcessor() { diff --git a/v2/internal/frontend/desktop/linux/frontend.go b/v2/internal/frontend/desktop/linux/frontend.go index f8c83eac1..e02c6a928 100644 --- a/v2/internal/frontend/desktop/linux/frontend.go +++ b/v2/internal/frontend/desktop/linux/frontend.go @@ -466,7 +466,6 @@ var requestBuffer = make(chan webview.Request, 100) func (f *Frontend) startRequestProcessor() { for request := range requestBuffer { f.assets.ServeWebViewRequest(request) - request.Release() } } diff --git a/v2/internal/frontend/devserver/devserver.go b/v2/internal/frontend/devserver/devserver.go index 47dde2953..35b67e86a 100644 --- a/v2/internal/frontend/devserver/devserver.go +++ b/v2/internal/frontend/devserver/devserver.go @@ -10,13 +10,11 @@ import ( "encoding/json" "fmt" "log" - "net" "net/http" "net/http/httputil" "net/url" "strings" "sync" - "time" "github.com/wailsapp/wails/v2/pkg/assetserver" @@ -67,7 +65,6 @@ func (d *DevWebServer) Run(ctx context.Context) error { myLogger = _logger.(*logger.Logger) } - var assetHandler http.Handler var wsHandler http.Handler _fronendDevServerURL, _ := ctx.Value("frontenddevserverurl").(string) @@ -77,33 +74,23 @@ func (d *DevWebServer) Run(ctx context.Context) error { return c.String(http.StatusOK, assetdir) }) - var err error - assetHandler, err = assetserver.NewAssetHandler(assetServerConfig, myLogger) - if err != nil { - log.Fatal(err) - } } else { externalURL, err := url.Parse(_fronendDevServerURL) if err != nil { return err } - if externalURL.Host == "" { - return fmt.Errorf("Invalid frontend:dev:serverUrl missing protocol scheme?") - } - - waitCb := func() { d.LogDebug("Waiting for frontend DevServer '%s' to be ready", externalURL) } - if !checkPortIsOpen(externalURL.Host, time.Minute, waitCb) { - d.logger.Error("Timeout waiting for frontend DevServer") - } - - assetHandler = newExternalDevServerAssetHandler(d.logger, externalURL, assetServerConfig) // WebSockets aren't currently supported in prod mode, so a WebSocket connection is the result of the // FrontendDevServer e.g. Vite to support auto reloads. // Therefore we direct WebSockets directly to the FrontendDevServer instead of returning a NotImplementedStatus. wsHandler = httputil.NewSingleHostReverseProxy(externalURL) } + assetHandler, err := assetserver.NewAssetHandler(assetServerConfig, myLogger) + if err != nil { + log.Fatal(err) + } + // Setup internal dev server bindingsJSON, err := d.appBindings.ToJSON() if err != nil { @@ -307,22 +294,3 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger. result.server.HidePort = true return result } - -func checkPortIsOpen(host string, timeout time.Duration, waitCB func()) (ret bool) { - if timeout == 0 { - timeout = time.Minute - } - - deadline := time.Now().Add(timeout) - for time.Now().Before(deadline) { - conn, _ := net.DialTimeout("tcp", host, 2*time.Second) - if conn != nil { - conn.Close() - return true - } - - waitCB() - time.Sleep(1 * time.Second) - } - return false -} diff --git a/v2/internal/frontend/devserver/external.go b/v2/pkg/assetserver/assethandler_external.go similarity index 68% rename from v2/internal/frontend/devserver/external.go rename to v2/pkg/assetserver/assethandler_external.go index fd717e723..588b350f5 100644 --- a/v2/internal/frontend/devserver/external.go +++ b/v2/pkg/assetserver/assethandler_external.go @@ -1,7 +1,7 @@ //go:build dev // +build dev -package devserver +package assetserver import ( "errors" @@ -10,21 +10,12 @@ import ( "net/http/httputil" "net/url" - "github.com/wailsapp/wails/v2/internal/logger" "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) -func newExternalDevServerAssetHandler(logger *logger.Logger, url *url.URL, options assetserver.Options) http.Handler { - handler := newExternalAssetsHandler(logger, url, options.Handler) +func NewExternalAssetsHandler(logger Logger, options assetserver.Options, url *url.URL) http.Handler { + baseHandler := options.Handler - if middleware := options.Middleware; middleware != nil { - handler = middleware(handler) - } - - return handler -} - -func newExternalAssetsHandler(logger *logger.Logger, url *url.URL, handler http.Handler) http.Handler { errSkipProxy := fmt.Errorf("skip proxying") proxy := httputil.NewSingleHostReverseProxy(url) @@ -37,7 +28,7 @@ func newExternalAssetsHandler(logger *logger.Logger, url *url.URL, handler http. } proxy.ModifyResponse = func(res *http.Response) error { - if handler == nil { + if baseHandler == nil { return nil } @@ -53,11 +44,11 @@ func newExternalAssetsHandler(logger *logger.Logger, url *url.URL, handler http. } proxy.ErrorHandler = func(rw http.ResponseWriter, r *http.Request, err error) { - if handler != nil && errors.Is(err, errSkipProxy) { + if baseHandler != nil && errors.Is(err, errSkipProxy) { if logger != nil { - logger.Debug("[ExternalAssetHandler] Loading '%s' failed, using AssetHandler", r.URL) + logger.Debug("[ExternalAssetHandler] Loading '%s' failed, using original AssetHandler", r.URL) } - handler.ServeHTTP(rw, r) + baseHandler.ServeHTTP(rw, r) } else { if logger != nil { logger.Error("[ExternalAssetHandler] Proxy error: %v", err) @@ -66,18 +57,24 @@ func newExternalAssetsHandler(logger *logger.Logger, url *url.URL, handler http. } } - return http.HandlerFunc( + var result http.Handler = http.HandlerFunc( func(rw http.ResponseWriter, req *http.Request) { if req.Method == http.MethodGet { proxy.ServeHTTP(rw, req) return } - if handler != nil { - handler.ServeHTTP(rw, req) + if baseHandler != nil { + baseHandler.ServeHTTP(rw, req) return } rw.WriteHeader(http.StatusMethodNotAllowed) }) + + if middleware := options.Middleware; middleware != nil { + result = middleware(result) + } + + return result } diff --git a/v2/pkg/assetserver/assetserver_legacy.go b/v2/pkg/assetserver/assetserver_legacy.go index 2d315aca3..4df671bc2 100644 --- a/v2/pkg/assetserver/assetserver_legacy.go +++ b/v2/pkg/assetserver/assetserver_legacy.go @@ -56,13 +56,7 @@ func (r legacyRequest) Response() webview.ResponseWriter { return &legacyRequestNoOpCloserResponseWriter{r.rw} } -func (r legacyRequest) AddRef() error { - return nil -} - -func (r legacyRequest) Release() error { - return nil -} +func (r legacyRequest) Close() error { return nil } func (r *legacyRequest) request() (*http.Request, error) { if r.req != nil { @@ -81,6 +75,4 @@ type legacyRequestNoOpCloserResponseWriter struct { http.ResponseWriter } -func (*legacyRequestNoOpCloserResponseWriter) Finish() error { - return nil -} +func (*legacyRequestNoOpCloserResponseWriter) Finish() {} diff --git a/v2/pkg/assetserver/assetserver_webview.go b/v2/pkg/assetserver/assetserver_webview.go index 3a7178c20..ae85f2513 100644 --- a/v2/pkg/assetserver/assetserver_webview.go +++ b/v2/pkg/assetserver/assetserver_webview.go @@ -22,6 +22,7 @@ type assetServerWebView struct { // ServeWebViewRequest processes the HTTP Request asynchronously by faking a golang HTTP Server. // The request will be finished with a StatusNotImplemented code if no handler has written to the response. +// The AssetServer takes ownership of the request and the caller mustn't close it or access it in any other way. func (d *AssetServer) ServeWebViewRequest(req webview.Request) { d.dispatchInit.Do(func() { workers := d.dispatchWorkers @@ -33,8 +34,11 @@ func (d *AssetServer) ServeWebViewRequest(req webview.Request) { for i := 0; i < workers; i++ { go func() { for req := range workerC { + uri, _ := req.URL() d.processWebViewRequest(req) - req.Release() + if err := req.Close(); err != nil { + d.logError("Unable to call close for request for uri '%s'", uri) + } } }() } @@ -45,12 +49,6 @@ func (d *AssetServer) ServeWebViewRequest(req webview.Request) { d.dispatchReqC = dispatchC }) - if err := req.AddRef(); err != nil { - uri, _ := req.URL() - d.logError("Unable to call AddRef for request '%s'", uri) - return - } - d.dispatchReqC <- req } diff --git a/v2/pkg/assetserver/webview/request.go b/v2/pkg/assetserver/webview/request.go index b0ce3d069..18ff29890 100644 --- a/v2/pkg/assetserver/webview/request.go +++ b/v2/pkg/assetserver/webview/request.go @@ -13,6 +13,5 @@ type Request interface { Response() ResponseWriter - AddRef() error - Release() error + Close() error } diff --git a/v2/pkg/assetserver/webview/request_darwin.go b/v2/pkg/assetserver/webview/request_darwin.go index 4f4919fab..653c19506 100644 --- a/v2/pkg/assetserver/webview/request_darwin.go +++ b/v2/pkg/assetserver/webview/request_darwin.go @@ -118,11 +118,9 @@ import ( ) // NewRequest creates as new WebViewRequest based on a pointer to an `id` -// -// Please make sure to call Release() when finished using the request. func NewRequest(wkURLSchemeTask unsafe.Pointer) Request { C.URLSchemeTaskRetain(wkURLSchemeTask) - return &request{task: wkURLSchemeTask} + return newRequestFinalizer(&request{task: wkURLSchemeTask}) } var _ Request = &request{} @@ -135,16 +133,6 @@ type request struct { rw *responseWriter } -func (r *request) AddRef() error { - C.URLSchemeTaskRetain(r.task) - return nil -} - -func (r *request) Release() error { - C.URLSchemeTaskRelease(r.task) - return nil -} - func (r *request) URL() (string, error) { return C.GoString(C.URLSchemeTaskRequestURL(r.task)), nil } @@ -205,6 +193,16 @@ func (r *request) Response() ResponseWriter { return r.rw } +func (r *request) Close() error { + var err error + if r.body != nil { + err = r.body.Close() + } + r.Response().Finish() + C.URLSchemeTaskRelease(r.task) + return err +} + var _ io.ReadCloser = &requestBodyStreamReader{} type requestBodyStreamReader struct { diff --git a/v2/pkg/assetserver/webview/request_finalizer.go b/v2/pkg/assetserver/webview/request_finalizer.go new file mode 100644 index 000000000..6a8c6a928 --- /dev/null +++ b/v2/pkg/assetserver/webview/request_finalizer.go @@ -0,0 +1,40 @@ +package webview + +import ( + "runtime" + "sync/atomic" +) + +var _ Request = &requestFinalizer{} + +type requestFinalizer struct { + Request + closed int32 +} + +// newRequestFinalizer returns a request with a runtime finalizer to make sure it will be closed from the finalizer +// if it has not been already closed. +// It also makes sure Close() of the wrapping request is only called once. +func newRequestFinalizer(r Request) Request { + rf := &requestFinalizer{Request: r} + // Make sure to async release since it might block the finalizer goroutine for a longer period + runtime.SetFinalizer(rf, func(obj *requestFinalizer) { rf.close(true) }) + return rf +} + +func (r *requestFinalizer) Close() error { + return r.close(false) +} + +func (r *requestFinalizer) close(asyncRelease bool) error { + if atomic.CompareAndSwapInt32(&r.closed, 0, 1) { + runtime.SetFinalizer(r, nil) + if asyncRelease { + go r.Request.Close() + return nil + } else { + return r.Request.Close() + } + } + return nil +} diff --git a/v2/pkg/assetserver/webview/request_linux.go b/v2/pkg/assetserver/webview/request_linux.go index ff758a065..101ee12fb 100644 --- a/v2/pkg/assetserver/webview/request_linux.go +++ b/v2/pkg/assetserver/webview/request_linux.go @@ -18,13 +18,12 @@ import ( ) // NewRequest creates as new WebViewRequest based on a pointer to an `WebKitURISchemeRequest` -// -// Please make sure to call Release() when finished using the request. func NewRequest(webKitURISchemeRequest unsafe.Pointer) Request { webkitReq := (*C.WebKitURISchemeRequest)(webKitURISchemeRequest) + C.g_object_ref(C.gpointer(webkitReq)) + req := &request{req: webkitReq} - req.AddRef() - return req + return newRequestFinalizer(req) } var _ Request = &request{} @@ -37,16 +36,6 @@ type request struct { rw *responseWriter } -func (r *request) AddRef() error { - C.g_object_ref(C.gpointer(r.req)) - return nil -} - -func (r *request) Release() error { - C.g_object_unref(C.gpointer(r.req)) - return nil -} - func (r *request) URL() (string, error) { return C.GoString(C.webkit_uri_scheme_request_get_uri(r.req)), nil } @@ -82,3 +71,13 @@ func (r *request) Response() ResponseWriter { r.rw = &responseWriter{req: r.req} return r.rw } + +func (r *request) Close() error { + var err error + if r.body != nil { + err = r.body.Close() + } + r.Response().Finish() + C.g_object_unref(C.gpointer(r.req)) + return err +} diff --git a/v2/pkg/assetserver/webview/responsewriter.go b/v2/pkg/assetserver/webview/responsewriter.go index 9e3c1952f..d67802a05 100644 --- a/v2/pkg/assetserver/webview/responsewriter.go +++ b/v2/pkg/assetserver/webview/responsewriter.go @@ -20,6 +20,6 @@ var ( type ResponseWriter interface { http.ResponseWriter - // Finish the response and flush all data. - Finish() error + // Finish the response and flush all data. A Finish after the request has already been finished has no effect. + Finish() } diff --git a/v2/pkg/assetserver/webview/responsewriter_darwin.go b/v2/pkg/assetserver/webview/responsewriter_darwin.go index 77de3c455..1c0cbee72 100644 --- a/v2/pkg/assetserver/webview/responsewriter_darwin.go +++ b/v2/pkg/assetserver/webview/responsewriter_darwin.go @@ -133,16 +133,15 @@ func (rw *responseWriter) WriteHeader(code int) { C.URLSchemeTaskDidReceiveResponse(rw.r.task, C.int(code), headers, C.int(headersLen)) } -func (rw *responseWriter) Finish() error { +func (rw *responseWriter) Finish() { if !rw.wroteHeader { rw.WriteHeader(http.StatusNotImplemented) } if rw.finished { - return nil + return } rw.finished = true C.URLSchemeTaskDidFinish(rw.r.task) - return nil } diff --git a/v2/pkg/assetserver/webview/responsewriter_linux.go b/v2/pkg/assetserver/webview/responsewriter_linux.go index 52e28aa5d..9b3f53a78 100644 --- a/v2/pkg/assetserver/webview/responsewriter_linux.go +++ b/v2/pkg/assetserver/webview/responsewriter_linux.go @@ -84,19 +84,18 @@ func (rw *responseWriter) WriteHeader(code int) { } } -func (rw *responseWriter) Finish() error { +func (rw *responseWriter) Finish() { if !rw.wroteHeader { rw.WriteHeader(http.StatusNotImplemented) } if rw.finished { - return nil + return } rw.finished = true if rw.w != nil { rw.w.Close() } - return nil } func (rw *responseWriter) finishWithError(code int, err error) { diff --git a/v2/pkg/menu/menuroles.go b/v2/pkg/menu/menuroles.go index 62a193c8e..e6b15b243 100644 --- a/v2/pkg/menu/menuroles.go +++ b/v2/pkg/menu/menuroles.go @@ -8,8 +8,9 @@ type Role int // These constants need to be kept in sync with `v2/internal/frontend/desktop/darwin/Role.h` const ( - AppMenuRole Role = 1 - EditMenuRole = 2 + AppMenuRole Role = 1 + EditMenuRole = 2 + WindowMenuRole = 3 //AboutRole Role = "about" //UndoRole Role = "undo" //RedoRole Role = "redo" @@ -142,14 +143,16 @@ func ViewMenu() *MenuItem { Role: ViewMenuRole, } } +*/ // WindowMenu provides a MenuItem with the whole default "Window" menu (Minimize, Zoom, etc.). +// On MacOS currently all options in there won't work if the window is frameless. func WindowMenu() *MenuItem { return &MenuItem{ Role: WindowMenuRole, } } -*/ + // These roles are Mac only // AppMenu provides a MenuItem with the whole default "App" menu (About, Services, etc.) diff --git a/v2/pkg/options/options.go b/v2/pkg/options/options.go index 204a267c6..74b2aef72 100644 --- a/v2/pkg/options/options.go +++ b/v2/pkg/options/options.go @@ -160,10 +160,14 @@ func processMenus(appoptions *App) { switch runtime.GOOS { case "darwin": if appoptions.Menu == nil { - appoptions.Menu = menu.NewMenuFromItems( - menu.AppMenu(), + items := []*menu.MenuItem{ menu.EditMenu(), - ) + } + if !appoptions.Frameless { + items = append(items, menu.WindowMenu()) // Current options in Window Menu only work if not frameless + } + + appoptions.Menu = menu.NewMenuFromItems(menu.AppMenu(), items...) } } } diff --git a/v2/pkg/templates/templates/svelte-ts/frontend/package.json b/v2/pkg/templates/templates/svelte-ts/frontend/package.json index 8bbb15b1b..2ee69eaf5 100644 --- a/v2/pkg/templates/templates/svelte-ts/frontend/package.json +++ b/v2/pkg/templates/templates/svelte-ts/frontend/package.json @@ -17,6 +17,6 @@ "svelte-preprocess": "^4.10.7", "tslib": "^2.4.0", "typescript": "^4.6.4", - "vite": "^3.0.0" + "vite": "^3.0.7" } } \ No newline at end of file diff --git a/v2/pkg/templates/templates/svelte/frontend/package.json b/v2/pkg/templates/templates/svelte/frontend/package.json index 8a9354150..8c9ae62a8 100644 --- a/v2/pkg/templates/templates/svelte/frontend/package.json +++ b/v2/pkg/templates/templates/svelte/frontend/package.json @@ -11,6 +11,6 @@ "devDependencies": { "@sveltejs/vite-plugin-svelte": "^1.0.1", "svelte": "^3.49.0", - "vite": "^3.0.0" + "vite": "^3.0.7" } } \ No newline at end of file diff --git a/v2/pkg/templates/templates/vanilla-ts/frontend/package.json b/v2/pkg/templates/templates/vanilla-ts/frontend/package.json index 37305b4c0..c57eb8610 100644 --- a/v2/pkg/templates/templates/vanilla-ts/frontend/package.json +++ b/v2/pkg/templates/templates/vanilla-ts/frontend/package.json @@ -9,6 +9,6 @@ }, "devDependencies": { "typescript": "^4.5.4", - "vite": "^2.9.9" + "vite": "^3.0.7" } } \ No newline at end of file diff --git a/v2/pkg/templates/templates/vanilla/frontend/package.json b/v2/pkg/templates/templates/vanilla/frontend/package.json index 4ac881798..a1b6f8e1a 100644 --- a/v2/pkg/templates/templates/vanilla/frontend/package.json +++ b/v2/pkg/templates/templates/vanilla/frontend/package.json @@ -8,6 +8,6 @@ "preview": "vite preview" }, "devDependencies": { - "vite": "^2.9.9" + "vite": "^3.0.7" } } \ No newline at end of file diff --git a/v3/STATUS.md b/v3/STATUS.md index 148c217b8..0bdd58d3f 100644 --- a/v3/STATUS.md +++ b/v3/STATUS.md @@ -8,72 +8,71 @@ Application interface methods | Method | Windows | Linux | Mac | Notes | |---------------------------------------------------------------|---------|-------|-----|-------| -| run() error | | | ✅ | | -| destroy() | | | ✅ | | -| setApplicationMenu(menu *Menu) | | | ✅ | | -| name() string | | | ✅ | | -| getCurrentWindowID() uint | | | ✅ | | -| showAboutDialog(name string, description string, icon []byte) | | | ✅ | | -| setIcon(icon []byte) | | | ✅ | | -| on(id uint) | | | ✅ | | -| dispatchOnMainThread(id uint) | | | ✅ | | -| hide() | | | ✅ | | -| show() | | | ✅ | | -| getPrimaryScreen() (*Screen, error) | | | ✅ | | -| getScreens() ([]*Screen, error) | | | ✅ | | +| run() error | | | Y | | +| destroy() | | | Y | | +| setApplicationMenu(menu *Menu) | | | Y | | +| name() string | | | Y | | +| getCurrentWindowID() uint | | | Y | | +| showAboutDialog(name string, description string, icon []byte) | | | Y | | +| setIcon(icon []byte) | | | Y | | +| on(id uint) | | | Y | | +| dispatchOnMainThread(fn func()) | Y | | Y | | +| hide() | Y | | Y | | +| show() | Y | | Y | | +| getPrimaryScreen() (*Screen, error) | | | Y | | +| getScreens() ([]*Screen, error) | | | Y | | ## Webview Window Webview Window Interface Methods -| Method | Windows | Linux | Mac | Notes | -|----------------------------------------------------|---------|-------|-----|-------| -| setTitle(title string) | | | ✅ | | -| setSize(width, height int) | | | ✅ | | -| setAlwaysOnTop(alwaysOnTop bool) | | | ✅ | | -| setURL(url string) | | | ✅ | | -| setResizable(resizable bool) | | | ✅ | | -| setMinSize(width, height int) | | | ✅ | | -| setMaxSize(width, height int) | | | ✅ | | -| execJS(js string) | | | ✅ | | -| restore() | | | ✅ | | -| setBackgroundColour(color *RGBA) | | | ✅ | | -| run() | | | ✅ | | -| center() | | | ✅ | | -| size() (int, int) | | | ✅ | | -| width() int | | | ✅ | | -| height() int | | | ✅ | | -| position() (int, int) | | | ✅ | | -| destroy() | | | ✅ | | -| reload() | | | ✅ | | -| forceReload() | | | ✅ | | -| toggleDevTools() | | | ✅ | | -| zoomReset() | | | ✅ | | -| zoomIn() | | | ✅ | | -| zoomOut() | | | ✅ | | -| getZoom() float64 | | | ✅ | | -| setZoom(zoom float64) | | | ✅ | | -| close() | | | ✅ | | -| zoom() | | | ✅ | | -| setHTML(html string) | | | ✅ | | -| setPosition(x int, y int) | | | ✅ | | -| on(eventID uint) | | | ✅ | | -| minimise() | | | ✅ | | -| unminimise() | | | ✅ | | -| maximise() | | | ✅ | | -| unmaximise() | | | ✅ | | -| fullscreen() | | | ✅ | | -| unfullscreen() | | | ✅ | | -| isMinimised() bool | | | ✅ | | -| isMaximised() bool | | | ✅ | | -| isFullscreen() bool | | | ✅ | | -| disableSizeConstraints() | | | ✅ | | -| setFullscreenButtonEnabled(enabled bool) | | | ✅ | | -| show() | | | ✅ | | -| hide() | | | ✅ | | -| getScreen() (*Screen, error) | | | ✅ | | -| setFrameless(bool) | | | ✅ | | -| openContextMenu(menu *Menu, data *ContextMenuData) | | | ✅ | | +| Method | Windows | Linux | Mac | Notes | +|----------------------------------------------------|---------|-------|-----|------------------------------------------| +| center() | Y | | Y | | +| close() | | | Y | | +| destroy() | | | Y | | +| execJS(js string) | | | Y | | +| forceReload() | | | Y | | +| fullscreen() | Y | | Y | | +| getScreen() (*Screen, error) | | | Y | | +| getZoom() float64 | | | Y | | +| height() int | Y | | Y | | +| hide() | Y | | Y | | +| isFullscreen() bool | Y | | Y | | +| isMaximised() bool | Y | | Y | | +| isMinimised() bool | Y | | Y | | +| maximise() | Y | | Y | | +| minimise() | Y | | Y | | +| nativeWindowHandle() (uintptr, error) | Y | | | | +| on(eventID uint) | | | Y | | +| openContextMenu(menu *Menu, data *ContextMenuData) | | | Y | | +| position() (int, int) | Y | | Y | | +| reload() | | | Y | | +| run() | Y | | Y | | +| setAlwaysOnTop(alwaysOnTop bool) | Y | | Y | | +| setBackgroundColour(color RGBA) | Y | | Y | | +| setFrameless(bool) | | | Y | | +| setFullscreenButtonEnabled(enabled bool) | - | | Y | There is no fullscreen button in Windows | +| setHTML(html string) | | | Y | | +| setMaxSize(width, height int) | Y | | Y | | +| setMinSize(width, height int) | Y | | Y | | +| setPosition(x int, y int) | Y | | Y | | +| setResizable(resizable bool) | Y | | Y | | +| setSize(width, height int) | Y | | Y | | +| setTitle(title string) | Y | | Y | | +| setURL(url string) | | | Y | | +| setZoom(zoom float64) | | | Y | | +| show() | Y | | Y | | +| size() (int, int) | Y | | Y | | +| toggleDevTools() | | | Y | | +| unfullscreen() | Y | | Y | | +| unmaximise() | Y | | Y | | +| unminimise() | Y | | Y | | +| width() int | Y | | Y | | +| zoom() | | | Y | | +| zoomIn() | | | Y | | +| zoomOut() | | | Y | | +| zoomReset() | | | Y | | ## Runtime @@ -81,73 +80,111 @@ Webview Window Interface Methods | Feature | Windows | Linux | Mac | Notes | |---------|---------|-------|-----|-------| -| Quit | | | ✅ | | -| Hide | | | ✅ | | -| Show | | | ✅ | | +| Quit | | | Y | | +| Hide | Y | | Y | | +| Show | Y | | Y | | ### Dialogs | Feature | Windows | Linux | Mac | Notes | |----------|---------|-------|-----|-------| -| Info | | | ✅ | | -| Warning | | | ✅ | | -| Error | | | ✅ | | -| Question | | | ✅ | | -| OpenFile | | | ✅ | | -| SaveFile | | | ✅ | | +| Info | | | Y | | +| Warning | | | Y | | +| Error | | | Y | | +| Question | | | Y | | +| OpenFile | | | Y | | +| SaveFile | | | Y | | ### Clipboard | Feature | Windows | Linux | Mac | Notes | |---------|---------|-------|-----|-------| -| SetText | | | ✅ | | -| Text | | | ✅ | | +| SetText | | | Y | | +| Text | | | Y | | ### ContextMenu | Feature | Windows | Linux | Mac | Notes | |-----------------|---------|-------|-----|-------| -| OpenContextMenu | | | ✅ | | +| OpenContextMenu | | | Y | | ### Screens | Feature | Windows | Linux | Mac | Notes | |------------|---------|-------|-----|-------| -| GetAll | | | ✅ | | -| GetPrimary | | | ✅ | | -| GetCurrent | | | ✅ | | +| GetAll | | | Y | | +| GetPrimary | | | Y | | +| GetCurrent | | | Y | | ### Window | Feature | Windows | Linux | Mac | Notes | |---------------------|---------|-------|-----|--------------------------------------------------------------------------------------| -| SetTitle | | | ✅ | | -| SetSize | | | ✅ | | -| Size | | | ✅ | | -| SetPosition | | | ✅ | | -| Position | | | ✅ | | -| FullScreen | | | ✅ | | -| UnFullscreen | | | ✅ | | -| Minimise | | | ✅ | | -| UnMinimise | | | ✅ | | -| Maximise | | | ✅ | | -| UnMaximise | | | ✅ | | -| Show | | | ✅ | | -| Hide | | | ✅ | | -| Center | | | ✅ | | -| SetBackgroundColour | | | ✅ | https://github.com/MicrosoftEdge/WebView2Feedback/issues/1621#issuecomment-938234294 | -| SetAlwaysOnTop | | | ✅ | | -| SetResizable | | | ✅ | | -| SetMinSize | | | ✅ | | -| SetMaxSize | | | ✅ | | -| Width | | | ✅ | | -| Height | | | ✅ | | -| ZoomIn | | | ✅ | Increase view scale | -| ZoomOut | | | ✅ | Decrease view scale | -| ZoomReset | | | ✅ | Reset view scale | -| GetZoom | | | ✅ | Get current view scale | -| SetZoom | | | ✅ | Set view scale | -| Screen | | | ✅ | Get screen for window | +| SetTitle | | | Y | | +| SetSize | | | Y | | +| Size | | | Y | | +| SetPosition | | | Y | | +| Position | | | Y | | +| FullScreen | | | Y | | +| UnFullscreen | | | Y | | +| Minimise | | | Y | | +| UnMinimise | | | Y | | +| Maximise | | | Y | | +| UnMaximise | | | Y | | +| Show | | | Y | | +| Hide | | | Y | | +| Center | | | Y | | +| SetBackgroundColour | | | Y | https://github.com/MicrosoftEdge/WebView2Feedback/issues/1621#issuecomment-938234294 | +| SetAlwaysOnTop | | | Y | | +| SetResizable | | | Y | | +| SetMinSize | | | Y | | +| SetMaxSize | | | Y | | +| Width | | | Y | | +| Height | | | Y | | +| ZoomIn | | | Y | Increase view scale | +| ZoomOut | | | Y | Decrease view scale | +| ZoomReset | | | Y | Reset view scale | +| GetZoom | | | Y | Get current view scale | +| SetZoom | | | Y | Set view scale | +| Screen | | | Y | Get screen for window | + + +### Window Options + +A 'Y' in the table below indicates that the option has been tested and is applied when the window is created. +An 'X' indicates that the option is not supported by the platform. + + +| Feature | Windows | Linux | Mac | Notes | +|---------------------------------|---------|-------|-----|--------------------------------------------| +| Name | | | | | +| Title | Y | | | | +| Width | Y | | | | +| Height | Y | | | | +| AlwaysOnTop | Y | | | | +| URL | | | | | +| DisableResize | Y | | | | +| Frameless | | | | | +| MinWidth | Y | | | | +| MinHeight | Y | | | | +| MaxWidth | Y | | | | +| MaxHeight | Y | | | | +| StartState | Y | | | | +| Mac | - | - | | | +| BackgroundType | | | | Acrylic seems to work but the others don't | +| BackgroundColour | Y | | | | +| HTML | | | | | +| JS | | | | | +| CSS | | | | | +| X | | | | | +| Y | | | | | +| HideOnClose | | | | | +| FullscreenButtonEnabled | | | | | +| Hidden | | | | | +| EnableFraudulentWebsiteWarnings | | | | | +| Zoom | | | | | +| EnableDragAndDrop | | | | | +| Windows | | - | - | | ### Log @@ -157,16 +194,16 @@ To log or not to log? System logger vs custom logger. | Event | Windows | Linux | Mac | Notes | |--------------------------|---------|-------|-----|-------| -| Default Application Menu | | | ✅ | | +| Default Application Menu | | | Y | | ## Tray Menus | Feature | Windows | Linux | Mac | Notes | |--------------------|---------|-------|-----|-------| -| Icon | | | ✅ | | -| Label | | | ✅ | | +| Icon | | | Y | | +| Label | | | Y | | | Label (ANSI Codes) | | | | | -| Menu | | | ✅ | | +| Menu | | | Y | | ## Cross Platform Events @@ -196,11 +233,11 @@ TBD ## Theme -| Plugin | Windows | Linux | Mac | Notes | -|-----------------|---------|-------|-----|-------| -| Dark | | | | | -| Light | | | | | -| System | | | | | +| Plugin | Windows | Linux | Mac | Notes | +|--------|---------|-------|-----|-------| +| Dark | Y | | | | +| Light | Y | | | | +| System | Y | | | | ## NSIS Installer @@ -216,31 +253,31 @@ Built-in plugin support: | Plugin | Windows | Linux | Mac | Notes | |-----------------|---------|-------|-----|-------| -| Browser | | | ✅ | | -| KV Store | | | ✅ | | -| Log | | | ✅ | | -| Single Instance | | | ✅ | | -| SQLite | | | ✅ | | -| Start at login | | | ✅ | | +| Browser | | | Y | | +| KV Store | | | Y | | +| Log | | | Y | | +| Single Instance | | | Y | | +| SQLite | | | Y | | +| Start at login | | | Y | | | Server | | | | | ## Packaging | | Windows | Linux | Mac | Notes | |-----------------|---------|-------|-----|-------| -| Icon Generation | | | ✅ | | -| Icon Embedding | | | ✅ | | -| Info.plist | | | ✅ | | -| NSIS Installer | | | | | -| Mac bundle | | | ✅ | | -| Windows exe | | | | | +| Icon Generation | | | Y | | +| Icon Embedding | | | Y | | +| Info.plist | | | Y | | +| NSIS Installer | | | - | | +| Mac bundle | | | Y | | +| Windows exe | | | - | | ## Frameless Windows | Feature | Windows | Linux | Mac | Notes | -|---------|---------|-------|----|-------| -| Resize | | | | | -| Drag | | | | | +|---------|---------|-------|-----|-------| +| Resize | | | | | +| Drag | | | | | ## Mac Specific @@ -248,4 +285,7 @@ Built-in plugin support: ## Windows Specific +- [x] Translucency +- [x] Custom Themes + ## Linux Specific diff --git a/v3/V3 Changes.md b/v3/V3 Changes.md index a002af2d5..f391029cf 100644 --- a/v3/V3 Changes.md +++ b/v3/V3 Changes.md @@ -180,3 +180,39 @@ const MyEnum = { - Why use `float64`? Can't we use `int`? - Because JavaScript doesn't have a concept of `int`. Everything is a `number`, which translates to `float64` in Go. There are also restrictions on casting types in Go's reflection package, which means using `int` doesn't work. + +### BackgroundColour + +In v2, this was a pointer to an `RGBA` struct. In v3, this is an `RGBA` struct value. + +### WindowIsTranslucent + +This flag has been removed. Now there is a `BackgroundType` flag that can be used to set the type of background the window should have. +This flag can be set to any of the following values: +- `BackgroundTypeSolid` - The window will have a solid background +- `BackgroundTypeTransparent` - The window will have a transparent background +- `BackgroundTypeTranslucent` - The window will have a translucent background + +On Windows, if the `BackgroundType` is set to `BackgroundTypeTranslucent`, the type of translucency can be set using the +`BackdropType` flag in the `WindowsWindow` options. This can be set to any of the following values: +- `Auto` - The window will use an effect determined by the system +- `None` - The window will have no background +- `Mica` - The window will use the Mica effect +- `Acrylic` - The window will use the acrylic effect +- `Tabbed` - The window will use the tabbed effect + + +## Windows Application Options + +### WndProcInterceptor + +If this is set, the WndProc will be intercepted and the function will be called. This allows you to handle Windows +messages directly. The function should have the following signature: + +```go +func(hwnd uintptr, msg uint32, wParam, lParam uintptr) (returnValue uintptr, shouldReturn) +``` + +The `shouldReturn` value should be set to `true` if the returnValue should be returned by the main wndProc method. +If it is set to `false`, the return value will be ignored and the message will continue to be processed by the main +wndProc method. \ No newline at end of file diff --git a/v3/examples/binding/go.mod b/v3/examples/binding/go.mod index affecaa9f..3dbff5f92 100644 --- a/v3/examples/binding/go.mod +++ b/v3/examples/binding/go.mod @@ -14,6 +14,7 @@ require ( github.com/wailsapp/wails/v2 v2.3.2-0.20230117193915-45c3a501d9e6 // indirect golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 // indirect golang.org/x/net v0.7.0 // indirect + golang.org/x/sys v0.7.0 // indirect ) replace github.com/wailsapp/wails/v3 => ../.. diff --git a/v3/examples/binding/go.sum b/v3/examples/binding/go.sum index c06e0dbc6..5266584f2 100644 --- a/v3/examples/binding/go.sum +++ b/v3/examples/binding/go.sum @@ -27,6 +27,8 @@ golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/v3/examples/binding/main.go b/v3/examples/binding/main.go index ed0bd6331..24b148fb9 100644 --- a/v3/examples/binding/main.go +++ b/v3/examples/binding/main.go @@ -19,14 +19,15 @@ func main() { Mac: application.MacOptions{ ApplicationShouldTerminateAfterLastWindowClosed: true, }, - }) - - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ Assets: application.AssetOptions{ FS: assets, }, }) + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ + URL: "/", + }) + err := app.Run() if err != nil { diff --git a/v3/examples/build/main.go b/v3/examples/build/main.go index 48ba24088..5bb3f0df1 100755 --- a/v3/examples/build/main.go +++ b/v3/examples/build/main.go @@ -67,7 +67,7 @@ func main() { if runtime.GOOS == "darwin" { myMenu.Add("New WebviewWindow (MacTitleBarHiddenInset)"). OnClick(func(ctx *application.Context) { - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Mac: application.MacWindow{ TitleBar: application.MacTitleBarHiddenInset, InvisibleTitleBarHeight: 25, @@ -81,7 +81,7 @@ func main() { }) myMenu.Add("New WebviewWindow (MacTitleBarHiddenInsetUnified)"). OnClick(func(ctx *application.Context) { - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Mac: application.MacWindow{ TitleBar: application.MacTitleBarHiddenInsetUnified, InvisibleTitleBarHeight: 50, @@ -95,7 +95,7 @@ func main() { }) myMenu.Add("New WebviewWindow (MacTitleBarHidden)"). OnClick(func(ctx *application.Context) { - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Mac: application.MacWindow{ TitleBar: application.MacTitleBarHidden, InvisibleTitleBarHeight: 25, diff --git a/v3/examples/contextmenus/main.go b/v3/examples/contextmenus/main.go index a94b9d340..ad428e091 100644 --- a/v3/examples/contextmenus/main.go +++ b/v3/examples/contextmenus/main.go @@ -25,7 +25,7 @@ func main() { }, }) - mainWindow := app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + mainWindow := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Context Menu Demo", Mac: application.MacWindow{ Backdrop: application.MacBackdropTranslucent, @@ -34,7 +34,7 @@ func main() { }, }) - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Context Menu Demo", Mac: application.MacWindow{ Backdrop: application.MacBackdropTranslucent, diff --git a/v3/examples/drag-n-drop/main.go b/v3/examples/drag-n-drop/main.go index 1346a2bac..7558ce55d 100644 --- a/v3/examples/drag-n-drop/main.go +++ b/v3/examples/drag-n-drop/main.go @@ -25,7 +25,7 @@ func main() { }, }) - window := app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + window := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Drag-n-drop Demo", Mac: application.MacWindow{ Backdrop: application.MacBackdropTranslucent, diff --git a/v3/examples/events/main.go b/v3/examples/events/main.go index 7f0c23653..dcf142437 100644 --- a/v3/examples/events/main.go +++ b/v3/examples/events/main.go @@ -41,7 +41,7 @@ func main() { } }) - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Events Demo", Mac: application.MacWindow{ Backdrop: application.MacBackdropTranslucent, @@ -49,7 +49,7 @@ func main() { InvisibleTitleBarHeight: 50, }, }) - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Events Demo", Mac: application.MacWindow{ Backdrop: application.MacBackdropTranslucent, diff --git a/v3/examples/kitchensink/main.go b/v3/examples/kitchensink/main.go index 509da3415..7c8151b37 100644 --- a/v3/examples/kitchensink/main.go +++ b/v3/examples/kitchensink/main.go @@ -109,13 +109,13 @@ func main() { mySystray.SetMenu(myMenu) mySystray.SetIconPosition(application.NSImageLeading) - myWindow := app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + myWindow := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Kitchen Sink", Width: 600, Height: 400, AlwaysOnTop: true, DisableResize: false, - BackgroundColour: &application.RGBA{ + BackgroundColour: application.RGBA{ Red: 255, Green: 255, Blue: 255, @@ -184,7 +184,7 @@ func main() { */ var myWindow2 *application.WebviewWindow var myWindow2Lock sync.RWMutex - myWindow2 = app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + myWindow2 = app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "#2", Width: 1024, Height: 768, diff --git a/v3/examples/plain/main.go b/v3/examples/plain/main.go index bc8406e82..83cfdea11 100644 --- a/v3/examples/plain/main.go +++ b/v3/examples/plain/main.go @@ -4,6 +4,7 @@ import ( _ "embed" "log" "net/http" + "time" "github.com/wailsapp/wails/v3/pkg/application" ) @@ -23,7 +24,7 @@ func main() { }, }) // Create window - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; user-select: none; -ms-user-select: none; -webkit-user-select: none; } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ @@ -38,6 +39,21 @@ func main() { println("clicked") }) + go func() { + time.Sleep(5 * time.Second) + + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ + Title: "Plain Bundle new Window from GoRoutine", + Width: 500, + Height: 500, + Mac: application.MacWindow{ + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInsetUnified, + InvisibleTitleBarHeight: 50, + }, + }) + }() + err := app.Run() if err != nil { diff --git a/v3/examples/screen/main.go b/v3/examples/screen/main.go index e71f5c4be..566dca2cf 100644 --- a/v3/examples/screen/main.go +++ b/v3/examples/screen/main.go @@ -24,7 +24,7 @@ func main() { }, }) - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Screen Demo", Width: 800, Height: 600, diff --git a/v3/examples/window/main.go b/v3/examples/window/main.go index 7a6e174c5..c428a92fd 100644 --- a/v3/examples/window/main.go +++ b/v3/examples/window/main.go @@ -56,7 +56,7 @@ func main() { myMenu.Add("New WebviewWindow (Hide on Close"). SetAccelerator("CmdOrCtrl+H"). OnClick(func(ctx *application.Context) { - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{HideOnClose: true}). + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{HideOnClose: true}). SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). SetPosition(rand.Intn(1000), rand.Intn(800)). SetURL("https://wails.io"). @@ -66,7 +66,7 @@ func main() { myMenu.Add("New Frameless WebviewWindow"). SetAccelerator("CmdOrCtrl+F"). OnClick(func(ctx *application.Context) { - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ X: rand.Intn(1000), Y: rand.Intn(800), Frameless: true, @@ -79,7 +79,7 @@ func main() { if runtime.GOOS == "darwin" { myMenu.Add("New WebviewWindow (MacTitleBarHiddenInset)"). OnClick(func(ctx *application.Context) { - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Mac: application.MacWindow{ TitleBar: application.MacTitleBarHiddenInset, InvisibleTitleBarHeight: 25, @@ -93,7 +93,7 @@ func main() { }) myMenu.Add("New WebviewWindow (MacTitleBarHiddenInsetUnified)"). OnClick(func(ctx *application.Context) { - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Mac: application.MacWindow{ TitleBar: application.MacTitleBarHiddenInsetUnified, InvisibleTitleBarHeight: 50, @@ -107,7 +107,7 @@ func main() { }) myMenu.Add("New WebviewWindow (MacTitleBarHidden)"). OnClick(func(ctx *application.Context) { - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Mac: application.MacWindow{ TitleBar: application.MacTitleBarHidden, InvisibleTitleBarHeight: 25, diff --git a/v3/examples/windowjs/main.go b/v3/examples/windowjs/main.go index ff7f61cd7..d1c20642e 100644 --- a/v3/examples/windowjs/main.go +++ b/v3/examples/windowjs/main.go @@ -34,7 +34,7 @@ func main() { newWindow := func() { windowName := "WebviewWindow " + strconv.Itoa(windowCounter) - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Name: windowName, }). SetTitle(windowName). diff --git a/v3/examples/wml/main.go b/v3/examples/wml/main.go index 1a9f4723b..d615e0dfc 100644 --- a/v3/examples/wml/main.go +++ b/v3/examples/wml/main.go @@ -24,7 +24,7 @@ func main() { }, }) - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Wails ML Demo", Width: 800, Height: 600, diff --git a/v3/go.mod b/v3/go.mod index 4ffb85ce6..edc912849 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -17,6 +17,7 @@ require ( github.com/samber/lo v1.37.0 github.com/tc-hib/winres v0.1.6 github.com/wailsapp/wails/v2 v2.3.2-0.20230117193915-45c3a501d9e6 + golang.org/x/sys v0.7.0 modernc.org/sqlite v1.21.0 ) @@ -53,7 +54,6 @@ require ( golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/net v0.7.0 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.5.0 // indirect golang.org/x/term v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect golang.org/x/tools v0.1.12 // indirect diff --git a/v3/go.sum b/v3/go.sum index ff474ab93..8fa850495 100644 --- a/v3/go.sum +++ b/v3/go.sum @@ -182,8 +182,8 @@ golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/v3/internal/templates/_base/default/main.go.tmpl b/v3/internal/templates/_base/default/main.go.tmpl index 1bc6a4868..16f808e8a 100644 --- a/v3/internal/templates/_base/default/main.go.tmpl +++ b/v3/internal/templates/_base/default/main.go.tmpl @@ -20,7 +20,7 @@ func main() { }, }) // Create window - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ diff --git a/v3/internal/templates/lit-ts/main.go.tmpl b/v3/internal/templates/lit-ts/main.go.tmpl index 1bc6a4868..16f808e8a 100644 --- a/v3/internal/templates/lit-ts/main.go.tmpl +++ b/v3/internal/templates/lit-ts/main.go.tmpl @@ -20,7 +20,7 @@ func main() { }, }) // Create window - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ diff --git a/v3/internal/templates/lit/main.go.tmpl b/v3/internal/templates/lit/main.go.tmpl index 1bc6a4868..16f808e8a 100644 --- a/v3/internal/templates/lit/main.go.tmpl +++ b/v3/internal/templates/lit/main.go.tmpl @@ -20,7 +20,7 @@ func main() { }, }) // Create window - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ diff --git a/v3/internal/templates/preact-ts/main.go.tmpl b/v3/internal/templates/preact-ts/main.go.tmpl index 1bc6a4868..16f808e8a 100644 --- a/v3/internal/templates/preact-ts/main.go.tmpl +++ b/v3/internal/templates/preact-ts/main.go.tmpl @@ -20,7 +20,7 @@ func main() { }, }) // Create window - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ diff --git a/v3/internal/templates/preact/main.go.tmpl b/v3/internal/templates/preact/main.go.tmpl index 1bc6a4868..16f808e8a 100644 --- a/v3/internal/templates/preact/main.go.tmpl +++ b/v3/internal/templates/preact/main.go.tmpl @@ -20,7 +20,7 @@ func main() { }, }) // Create window - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ diff --git a/v3/internal/templates/react-swc-ts/main.go.tmpl b/v3/internal/templates/react-swc-ts/main.go.tmpl index 1bc6a4868..16f808e8a 100644 --- a/v3/internal/templates/react-swc-ts/main.go.tmpl +++ b/v3/internal/templates/react-swc-ts/main.go.tmpl @@ -20,7 +20,7 @@ func main() { }, }) // Create window - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ diff --git a/v3/internal/templates/react-swc/main.go.tmpl b/v3/internal/templates/react-swc/main.go.tmpl index 1bc6a4868..16f808e8a 100644 --- a/v3/internal/templates/react-swc/main.go.tmpl +++ b/v3/internal/templates/react-swc/main.go.tmpl @@ -20,7 +20,7 @@ func main() { }, }) // Create window - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ diff --git a/v3/internal/templates/react-ts/main.go.tmpl b/v3/internal/templates/react-ts/main.go.tmpl index 1bc6a4868..16f808e8a 100644 --- a/v3/internal/templates/react-ts/main.go.tmpl +++ b/v3/internal/templates/react-ts/main.go.tmpl @@ -20,7 +20,7 @@ func main() { }, }) // Create window - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ diff --git a/v3/internal/templates/react/main.go.tmpl b/v3/internal/templates/react/main.go.tmpl index 1bc6a4868..16f808e8a 100644 --- a/v3/internal/templates/react/main.go.tmpl +++ b/v3/internal/templates/react/main.go.tmpl @@ -20,7 +20,7 @@ func main() { }, }) // Create window - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ diff --git a/v3/internal/templates/svelte-ts/main.go.tmpl b/v3/internal/templates/svelte-ts/main.go.tmpl index 1bc6a4868..16f808e8a 100644 --- a/v3/internal/templates/svelte-ts/main.go.tmpl +++ b/v3/internal/templates/svelte-ts/main.go.tmpl @@ -20,7 +20,7 @@ func main() { }, }) // Create window - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ diff --git a/v3/internal/templates/svelte/main.go.tmpl b/v3/internal/templates/svelte/main.go.tmpl index 1bc6a4868..16f808e8a 100644 --- a/v3/internal/templates/svelte/main.go.tmpl +++ b/v3/internal/templates/svelte/main.go.tmpl @@ -20,7 +20,7 @@ func main() { }, }) // Create window - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ diff --git a/v3/internal/templates/vanilla-ts/main.go.tmpl b/v3/internal/templates/vanilla-ts/main.go.tmpl index 1bc6a4868..16f808e8a 100644 --- a/v3/internal/templates/vanilla-ts/main.go.tmpl +++ b/v3/internal/templates/vanilla-ts/main.go.tmpl @@ -20,7 +20,7 @@ func main() { }, }) // Create window - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ diff --git a/v3/internal/templates/vanilla/main.go.tmpl b/v3/internal/templates/vanilla/main.go.tmpl index 1bc6a4868..16f808e8a 100644 --- a/v3/internal/templates/vanilla/main.go.tmpl +++ b/v3/internal/templates/vanilla/main.go.tmpl @@ -20,7 +20,7 @@ func main() { }, }) // Create window - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ diff --git a/v3/internal/templates/vue-ts/main.go.tmpl b/v3/internal/templates/vue-ts/main.go.tmpl index 1bc6a4868..16f808e8a 100644 --- a/v3/internal/templates/vue-ts/main.go.tmpl +++ b/v3/internal/templates/vue-ts/main.go.tmpl @@ -20,7 +20,7 @@ func main() { }, }) // Create window - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ diff --git a/v3/internal/templates/vue/main.go.tmpl b/v3/internal/templates/vue/main.go.tmpl index 1bc6a4868..16f808e8a 100644 --- a/v3/internal/templates/vue/main.go.tmpl +++ b/v3/internal/templates/vue/main.go.tmpl @@ -20,7 +20,7 @@ func main() { }, }) // Create window - app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "Plain Bundle", CSS: `body { background-color: rgba(255, 255, 255, 0); } .main { color: white; margin: 20%; }`, Mac: application.MacWindow{ diff --git a/v3/pkg/application/application.go b/v3/pkg/application/application.go index b26d6a6f0..8a75cfa9a 100644 --- a/v3/pkg/application/application.go +++ b/v3/pkg/application/application.go @@ -8,6 +8,8 @@ import ( "strconv" "sync" + "github.com/samber/lo" + "github.com/wailsapp/wails/v2/pkg/assetserver" "github.com/wailsapp/wails/v2/pkg/assetserver/webview" assetserveroptions "github.com/wailsapp/wails/v2/pkg/options/assetserver" @@ -23,6 +25,10 @@ func init() { runtime.LockOSThread() } +type EventListener struct { + callback func() +} + func New(appOptions Options) *App { if globalApplication != nil { return globalApplication @@ -32,7 +38,7 @@ func New(appOptions Options) *App { result := &App{ options: appOptions, - applicationEventListeners: make(map[uint][]func()), + applicationEventListeners: make(map[uint][]*EventListener), systemTrays: make(map[uint]*SystemTray), log: logger.New(appOptions.Logger.CustomLoggers...), contextMenus: make(map[string]*Menu), @@ -155,7 +161,7 @@ var webviewRequests = make(chan *webViewAssetRequest) type App struct { options Options - applicationEventListeners map[uint][]func() + applicationEventListeners map[uint][]*EventListener applicationEventListenersLock sync.RWMutex // Windows @@ -216,17 +222,28 @@ func (a *App) deleteWindowByID(id uint) { delete(a.windows, id) } -func (a *App) On(eventType events.ApplicationEventType, callback func()) { +func (a *App) On(eventType events.ApplicationEventType, callback func()) func() { eventID := uint(eventType) a.applicationEventListenersLock.Lock() defer a.applicationEventListenersLock.Unlock() - a.applicationEventListeners[eventID] = append(a.applicationEventListeners[eventID], callback) + listener := &EventListener{ + callback: callback, + } + a.applicationEventListeners[eventID] = append(a.applicationEventListeners[eventID], listener) if a.impl != nil { go a.impl.on(eventID) } + + return func() { + // lock the map + a.applicationEventListenersLock.Lock() + defer a.applicationEventListenersLock.Unlock() + // Remove listener + a.applicationEventListeners[eventID] = lo.Without(a.applicationEventListeners[eventID], listener) + } } func (a *App) NewWebviewWindow() *WebviewWindow { - return a.NewWebviewWindowWithOptions(&WebviewWindowOptions{}) + return a.NewWebviewWindowWithOptions(WebviewWindowOptions{}) } func (a *App) GetPID() int { @@ -267,11 +284,7 @@ func (a *App) error(message string, args ...any) { }) } -func (a *App) NewWebviewWindowWithOptions(windowOptions *WebviewWindowOptions) *WebviewWindow { - // Ensure we have sane defaults - if windowOptions == nil { - windowOptions = WebviewWindowDefaults - } +func (a *App) NewWebviewWindowWithOptions(windowOptions WebviewWindowOptions) *WebviewWindow { newWindow := NewWindow(windowOptions) id := newWindow.id if a.windows == nil { @@ -327,10 +340,6 @@ func (a *App) Run() error { for { request := <-webviewRequests a.handleWebViewRequest(request) - err := request.Release() - if err != nil { - a.error("Failed to release webview request: %s", err.Error()) - } } }() go func() { @@ -364,10 +373,10 @@ func (a *App) Run() error { } // set the application menu - a.impl.setApplicationMenu(a.ApplicationMenu) - - // set the application Icon - a.impl.setIcon(a.options.Icon) + if runtime.GOOS == "darwin" { + a.impl.setApplicationMenu(a.ApplicationMenu) + a.impl.setIcon(a.options.Icon) + } err := a.impl.run() if err != nil { @@ -387,7 +396,7 @@ func (a *App) handleApplicationEvent(event uint) { return } for _, listener := range listeners { - go listener() + go listener.callback() } } @@ -606,3 +615,35 @@ func (a *App) GetWindowByName(name string) *WebviewWindow { } return nil } + +func invokeSync(fn func()) { + var wg sync.WaitGroup + wg.Add(1) + globalApplication.dispatchOnMainThread(func() { + fn() + wg.Done() + }) + wg.Wait() +} + +func invokeSyncWithResult[T any](fn func() T) (res T) { + var wg sync.WaitGroup + wg.Add(1) + globalApplication.dispatchOnMainThread(func() { + res = fn() + wg.Done() + }) + wg.Wait() + return res +} + +func invokeSyncWithResultAndError[T any](fn func() (T, error)) (res T, err error) { + var wg sync.WaitGroup + wg.Add(1) + globalApplication.dispatchOnMainThread(func() { + res, err = fn() + wg.Done() + }) + wg.Wait() + return res, err +} diff --git a/v3/pkg/application/application_windows.go b/v3/pkg/application/application_windows.go index de791ce58..2cf49bc80 100644 --- a/v3/pkg/application/application_windows.go +++ b/v3/pkg/application/application_windows.go @@ -2,14 +2,35 @@ package application -type windowsApp struct { - //applicationMenu unsafe.Pointer - parent *App -} +import ( + "os" + "syscall" + "unsafe" -func (m *windowsApp) dispatchOnMainThread(id uint) { - //TODO implement me - panic("implement me") + "github.com/wailsapp/wails/v3/pkg/events" + "github.com/wailsapp/wails/v3/pkg/w32" + + "github.com/samber/lo" +) + +var windowClassName = lo.Must(syscall.UTF16PtrFromString("WailsWebviewWindow")) + +type windowsApp struct { + parent *App + + instance w32.HINSTANCE + + windowMap map[w32.HWND]*windowsWebviewWindow + + mainThreadID w32.HANDLE + mainThreadWindowHWND w32.HWND + + // Windows hidden by application.Hide() + hiddenWindows []*windowsWebviewWindow + focusedWindow w32.HWND + + // system theme + isDarkMode bool } func (m *windowsApp) getPrimaryScreen() (*Screen, error) { @@ -23,11 +44,29 @@ func (m *windowsApp) getScreens() ([]*Screen, error) { } func (m *windowsApp) hide() { - //C.hide() + // Get the current focussed window + m.focusedWindow = w32.GetForegroundWindow() + + // Iterate over all windows and hide them if they aren't already hidden + for _, window := range m.windowMap { + if window.isVisible() { + // Add to hidden windows + m.hiddenWindows = append(m.hiddenWindows, window) + window.hide() + } + } + // Switch focus to the next application + hwndNext := w32.GetWindow(m.mainThreadWindowHWND, w32.GW_HWNDNEXT) + w32.SetForegroundWindow(hwndNext) } func (m *windowsApp) show() { - //C.show() + // Iterate over all windows and show them if they were previously hidden + for _, window := range m.hiddenWindows { + window.show() + } + // Show the foreground window + w32.SetForegroundWindow(m.focusedWindow) } func (m *windowsApp) on(eventID uint) { @@ -73,6 +112,9 @@ func (m *windowsApp) run() error { for eventID := range m.parent.applicationEventListeners { m.on(eventID) } + + _ = m.runMainLoop() + //C.run() return nil } @@ -81,9 +123,110 @@ func (m *windowsApp) destroy() { //C.destroyApp() } -func newPlatformApp(app *App) *windowsApp { - //C.init() - return &windowsApp{ - parent: app, +func (m *windowsApp) init() { + // Register the window class + + icon := w32.LoadIconWithResourceID(m.instance, w32.IDI_APPLICATION) + + var wc w32.WNDCLASSEX + wc.Size = uint32(unsafe.Sizeof(wc)) + wc.Style = w32.CS_HREDRAW | w32.CS_VREDRAW + wc.WndProc = syscall.NewCallback(m.wndProc) + wc.Instance = m.instance + wc.Background = w32.COLOR_BTNFACE + 1 + wc.Icon = icon + wc.Cursor = w32.LoadCursorWithResourceID(0, w32.IDC_ARROW) + wc.ClassName = windowClassName + wc.MenuName = nil + wc.IconSm = icon + + if ret := w32.RegisterClassEx(&wc); ret == 0 { + panic(syscall.GetLastError()) + } + + m.isDarkMode = w32.IsCurrentlyDarkMode() +} + +func (m *windowsApp) wndProc(hwnd w32.HWND, msg uint32, wParam, lParam uintptr) uintptr { + + // Handle the invoke callback + if msg == wmInvokeCallback { + m.invokeCallback(wParam, lParam) + return 0 + } + + // If the WndProcInterceptor is set in options, pass the message on + if m.parent.options.Windows.WndProcInterceptor != nil { + returnValue, shouldReturn := m.parent.options.Windows.WndProcInterceptor(hwnd, msg, wParam, lParam) + if shouldReturn { + return returnValue + } + } + + switch msg { + case w32.WM_SETTINGCHANGE: + settingChanged := w32.UTF16PtrToString((*uint16)(unsafe.Pointer(lParam))) + if settingChanged == "ImmersiveColorSet" { + isDarkMode := w32.IsCurrentlyDarkMode() + if isDarkMode != m.isDarkMode { + applicationEvents <- uint(events.Windows.SystemThemeChanged) + m.isDarkMode = isDarkMode + } + } + return 0 + case w32.WM_POWERBROADCAST: + switch wParam { + case w32.PBT_APMPOWERSTATUSCHANGE: + applicationEvents <- uint(events.Windows.APMPowerStatusChange) + case w32.PBT_APMSUSPEND: + applicationEvents <- uint(events.Windows.APMSuspend) + case w32.PBT_APMRESUMEAUTOMATIC: + applicationEvents <- uint(events.Windows.APMResumeAutomatic) + case w32.PBT_APMRESUMESUSPEND: + applicationEvents <- uint(events.Windows.APMResumeSuspend) + case w32.PBT_POWERSETTINGCHANGE: + applicationEvents <- uint(events.Windows.APMPowerSettingChange) + } + return 0 + } + + if window, ok := m.windowMap[hwnd]; ok { + return window.WndProc(msg, wParam, lParam) + } + + // Dispatch the message to the appropriate window + + return w32.DefWindowProc(hwnd, msg, wParam, lParam) +} + +func (m *windowsApp) registerWindow(result *windowsWebviewWindow) { + m.windowMap[result.hwnd] = result +} + +func (m *windowsApp) unregisterWindow(w *windowsWebviewWindow) { + delete(m.windowMap, w.hwnd) + + // If this was the last window... + if len(m.windowMap) == 0 { + w32.PostQuitMessage(0) } } + +func newPlatformApp(app *App) *windowsApp { + err := w32.SetProcessDPIAware() + if err != nil { + println("Fatal error in application initialisation: ", err.Error()) + os.Exit(1) + } + + result := &windowsApp{ + parent: app, + instance: w32.GetModuleHandle(""), + windowMap: make(map[w32.HWND]*windowsWebviewWindow), + } + + result.init() + result.initMainLoop() + + return result +} diff --git a/v3/pkg/application/mainthread_windows.go b/v3/pkg/application/mainthread_windows.go new file mode 100644 index 000000000..f2f03454c --- /dev/null +++ b/v3/pkg/application/mainthread_windows.go @@ -0,0 +1,126 @@ +//go:build windows + +package application + +import ( + "github.com/wailsapp/wails/v3/pkg/w32" + "runtime" + "sort" + "unsafe" +) + +var ( + wmInvokeCallback uint32 +) + +func init() { + wmInvokeCallback = w32.RegisterWindowMessage(w32.MustStringToUTF16Ptr("WailsV0.InvokeCallback")) +} + +// initMainLoop must be called with the same OSThread that is used to call runMainLoop() later. +func (m *windowsApp) initMainLoop() { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + if m.mainThreadWindowHWND != 0 { + panic("initMainLoop was already called") + } + + // We need a hidden window so we can PostMessage to it, if we don't use PostMessage for dispatching to a HWND + // messages might get lost if a modal inner loop is being run. + // We had this once in V2: https://github.com/wailsapp/wails/issues/969 + // See: https://devblogs.microsoft.com/oldnewthing/20050426-18/?p=35783 + // See also: https://learn.microsoft.com/en-us/windows/win32/winmsg/using-messages-and-message-queues#creating-a-message-loop + // > Because the system directs messages to individual windows in an application, a thread must create at least one window before starting its message loop. + m.mainThreadWindowHWND = w32.CreateWindowEx( + 0, + windowClassName, + w32.MustStringToUTF16Ptr("__wails_hidden_mainthread"), + w32.WS_DISABLED, + w32.CW_USEDEFAULT, + w32.CW_USEDEFAULT, + 0, + 0, + 0, + 0, + w32.GetModuleHandle(""), + nil) + + m.mainThreadID, _ = w32.GetWindowThreadProcessId(m.mainThreadWindowHWND) +} + +func (m *windowsApp) runMainLoop() int { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + if m.invokeRequired() { + panic("invokeRequired for runMainLoop, the mainloop must be running on the same OSThread as the mainThreadWindow has been created on") + } + + msg := (*w32.MSG)(unsafe.Pointer(w32.GlobalAlloc(0, uint32(unsafe.Sizeof(w32.MSG{}))))) + defer w32.GlobalFree(w32.HGLOBAL(unsafe.Pointer(m))) + + for w32.GetMessage(msg, 0, 0, 0) != 0 { + w32.TranslateMessage(msg) + w32.DispatchMessage(msg) + } + + return int(msg.WParam) +} + +func (m *windowsApp) dispatchOnMainThread(id uint) { + mainThreadHWND := m.mainThreadWindowHWND + if mainThreadHWND == 0 { + panic("initMainLoop was not called") + } + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + if m.invokeRequired() { + w32.PostMessage(mainThreadHWND, wmInvokeCallback, uintptr(id), 0) + } else { + mainThreadFunctionStoreLock.Lock() + fn := mainThreadFunctionStore[id] + delete(mainThreadFunctionStore, id) + mainThreadFunctionStoreLock.Unlock() + + if fn == nil { + Fatal("dispatchOnMainThread called with invalid id: %v", id) + } + fn() + } +} + +func (m *windowsApp) invokeRequired() bool { + mainThreadID := m.mainThreadID + if mainThreadID == 0 { + panic("initMainLoop was not called") + } + + return mainThreadID != w32.GetCurrentThreadId() +} + +func (m *windowsApp) invokeCallback(wParam, lParam uintptr) { + // TODO: Should we invoke just one or all queued? In v2 we always invoked all pendings... + runtime.LockOSThread() + defer runtime.UnlockOSThread() + if m.invokeRequired() { + panic("invokeCallback must always be called on the MainOSThread") + } + + mainThreadFunctionStoreLock.Lock() + fnIDs := make([]uint, 0, len(mainThreadFunctionStore)) + for id := range mainThreadFunctionStore { + fnIDs = append(fnIDs, id) + } + sort.Slice(fnIDs, func(i, j int) bool { return fnIDs[i] < fnIDs[j] }) + + fns := make([]func(), len(fnIDs)) + for i, id := range fnIDs { + fns[i] = mainThreadFunctionStore[id] + delete(mainThreadFunctionStore, id) + } + mainThreadFunctionStoreLock.Unlock() + + for _, fn := range fns { + fn() + } +} diff --git a/v3/pkg/application/menu.go b/v3/pkg/application/menu.go index e20f0e3a6..5a6139031 100644 --- a/v3/pkg/application/menu.go +++ b/v3/pkg/application/menu.go @@ -96,14 +96,3 @@ func (m *Menu) setContextData(data *ContextMenuData) { func (a *App) NewMenu() *Menu { return &Menu{} } - -func defaultApplicationMenu() *Menu { - menu := NewMenu() - menu.AddRole(AppMenu) - menu.AddRole(FileMenu) - menu.AddRole(EditMenu) - menu.AddRole(ViewMenu) - menu.AddRole(WindowMenu) - menu.AddRole(HelpMenu) - return menu -} diff --git a/v3/pkg/application/menu_darwin.go b/v3/pkg/application/menu_darwin.go index b14be232a..dc6493ad2 100644 --- a/v3/pkg/application/menu_darwin.go +++ b/v3/pkg/application/menu_darwin.go @@ -103,3 +103,14 @@ func (m *macosMenu) processMenu(parent unsafe.Pointer, menu *Menu) { } } + +func defaultApplicationMenu() *Menu { + menu := NewMenu() + menu.AddRole(AppMenu) + menu.AddRole(FileMenu) + menu.AddRole(EditMenu) + menu.AddRole(ViewMenu) + menu.AddRole(WindowMenu) + menu.AddRole(HelpMenu) + return menu +} diff --git a/v3/pkg/application/menu_windows.go b/v3/pkg/application/menu_windows.go index b60c0b35b..33cd608c2 100644 --- a/v3/pkg/application/menu_windows.go +++ b/v3/pkg/application/menu_windows.go @@ -50,3 +50,13 @@ func (m *windowsMenu) processMenu(parent unsafe.Pointer, menu *Menu) { // //} } + +func defaultApplicationMenu() *Menu { + menu := NewMenu() + menu.AddRole(FileMenu) + menu.AddRole(EditMenu) + menu.AddRole(ViewMenu) + menu.AddRole(WindowMenu) + menu.AddRole(HelpMenu) + return menu +} diff --git a/v3/pkg/application/menuitem.go b/v3/pkg/application/menuitem.go index 9b0ff3724..ed7143e29 100644 --- a/v3/pkg/application/menuitem.go +++ b/v3/pkg/application/menuitem.go @@ -173,6 +173,8 @@ func newRole(role Role) *MenuItem { return newMinimizeMenuItem() case Zoom: return newZoomMenuItem() + case FullScreen: + return newFullScreenMenuItem() default: println("No support for role:", role) diff --git a/v3/pkg/application/menuitem_darwin.go b/v3/pkg/application/menuitem_darwin.go index c918c2ee3..eb252abf1 100644 --- a/v3/pkg/application/menuitem_darwin.go +++ b/v3/pkg/application/menuitem_darwin.go @@ -606,7 +606,7 @@ func newMinimizeMenuItem() *MenuItem { OnClick(func(ctx *Context) { currentWindow := globalApplication.CurrentWindow() if currentWindow != nil { - currentWindow.Minimize() + currentWindow.Minimise() } }) } @@ -620,3 +620,13 @@ func newZoomMenuItem() *MenuItem { } }) } + +func newFullScreenMenuItem() *MenuItem { + return newMenuItem("Fullscreen"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.CurrentWindow() + if currentWindow != nil { + currentWindow.Fullscreen() + } + }) +} diff --git a/v3/pkg/application/menuitem_windows.go b/v3/pkg/application/menuitem_windows.go index c8974c26f..caf07b850 100644 --- a/v3/pkg/application/menuitem_windows.go +++ b/v3/pkg/application/menuitem_windows.go @@ -177,3 +177,7 @@ func newMinimizeMenuItem() *MenuItem { func newZoomMenuItem() *MenuItem { panic("implement me") } + +func newFullScreenMenuItem() *MenuItem { + panic("implement me") +} diff --git a/v3/pkg/application/messageprocessor_window.go b/v3/pkg/application/messageprocessor_window.go index cf36b9c31..7789cad7c 100644 --- a/v3/pkg/application/messageprocessor_window.go +++ b/v3/pkg/application/messageprocessor_window.go @@ -46,7 +46,7 @@ func (m *MessageProcessor) processWindowMethod(method string, rw http.ResponseWr window.UnFullscreen() m.ok(rw) case "Minimise": - window.Minimize() + window.Minimise() m.ok(rw) case "UnMinimise": window.UnMinimise() @@ -102,7 +102,7 @@ func (m *MessageProcessor) processWindowMethod(method string, rw http.ResponseWr m.Error("Invalid SetBackgroundColour Message: 'a' value required") return } - window.SetBackgroundColour(&RGBA{ + window.SetBackgroundColour(RGBA{ Red: *r, Green: *g, Blue: *b, diff --git a/v3/pkg/application/options_application.go b/v3/pkg/application/options_application.go index 0a768a0b7..f37b2dc7e 100644 --- a/v3/pkg/application/options_application.go +++ b/v3/pkg/application/options_application.go @@ -12,6 +12,7 @@ type Options struct { Description string Icon []byte Mac MacOptions + Windows WindowsApplicationOptions Bind []any Logger struct { Silent bool diff --git a/v3/pkg/application/options_webview_window.go b/v3/pkg/application/options_webview_window.go index 99e40d054..be0537947 100644 --- a/v3/pkg/application/options_webview_window.go +++ b/v3/pkg/application/options_webview_window.go @@ -12,7 +12,8 @@ const ( type WebviewWindowOptions struct { Name string Title string - Width, Height int + Width int + Height int AlwaysOnTop bool URL string DisableResize bool @@ -23,7 +24,8 @@ type WebviewWindowOptions struct { MaxHeight int StartState WindowState Mac MacWindow - BackgroundColour *RGBA + BackgroundType BackgroundType + BackgroundColour RGBA HTML string JS string CSS string @@ -35,6 +37,7 @@ type WebviewWindowOptions struct { EnableFraudulentWebsiteWarnings bool Zoom float64 EnableDragAndDrop bool + Windows WindowsWindow } var WebviewWindowDefaults = &WebviewWindowOptions{ @@ -42,8 +45,22 @@ var WebviewWindowDefaults = &WebviewWindowOptions{ Width: 800, Height: 600, URL: "", + BackgroundColour: RGBA{ + Red: 255, + Green: 255, + Blue: 255, + Alpha: 255, + }, } type RGBA struct { Red, Green, Blue, Alpha uint8 } + +type BackgroundType int + +const ( + BackgroundTypeSolid BackgroundType = iota + BackgroundTypeTransparent + BackgroundTypeTranslucent +) diff --git a/v3/pkg/application/options_win.go b/v3/pkg/application/options_win.go new file mode 100644 index 000000000..7e58cf9af --- /dev/null +++ b/v3/pkg/application/options_win.go @@ -0,0 +1,62 @@ +package application + +type WindowsApplicationOptions struct { + // WndProcInterceptor is a function that will be called for every message sent in the application. + // Use this to hook into the main message loop. This is useful for handling custom window messages. + // If `shouldReturn` is `true` then `returnCode` will be returned by the main message loop. + // If `shouldReturn` is `false` then returnCode will be ignored and the message will be processed by the main message loop. + WndProcInterceptor func(hwnd uintptr, msg uint32, wParam, lParam uintptr) (returnCode uintptr, shouldReturn bool) +} + +type BackdropType int32 + +const ( + Auto BackdropType = 0 + None BackdropType = 1 + Mica BackdropType = 2 + Acrylic BackdropType = 3 + Tabbed BackdropType = 4 +) + +type WindowsWindow struct { + // Select the type of translucent backdrop. Requires Windows 11 22621 or later. + BackdropType BackdropType + // Disable the icon in the titlebar + DisableIcon bool + // Theme. Defaults to SystemDefault which will use whatever the system theme is. The application will follow system theme changes. + Theme Theme + // Custom colours for dark/light mode + CustomTheme *ThemeSettings + + // Disable all window decorations in Frameless mode, which means no "Aero Shadow" and no "Rounded Corner" will be shown. + // "Rounded Corners" are only available on Windows 11. + DisableFramelessWindowDecorations bool +} + +type Theme int + +const ( + // SystemDefault will use whatever the system theme is. The application will follow system theme changes. + SystemDefault Theme = 0 + // Dark Mode + Dark Theme = 1 + // Light Mode + Light Theme = 2 +) + +// ThemeSettings defines custom colours to use in dark or light mode. +// They may be set using the hex values: 0x00BBGGRR +type ThemeSettings struct { + DarkModeTitleBar int32 + DarkModeTitleBarInactive int32 + DarkModeTitleText int32 + DarkModeTitleTextInactive int32 + DarkModeBorder int32 + DarkModeBorderInactive int32 + LightModeTitleBar int32 + LightModeTitleBarInactive int32 + LightModeTitleText int32 + LightModeTitleTextInactive int32 + LightModeBorder int32 + LightModeBorderInactive int32 +} diff --git a/v3/pkg/application/roles.go b/v3/pkg/application/roles.go index 0aec3bd48..fe48a0d1e 100644 --- a/v3/pkg/application/roles.go +++ b/v3/pkg/application/roles.go @@ -42,8 +42,9 @@ const ( ZoomOut Role = iota ToggleFullscreen Role = iota - Minimize Role = iota - Zoom Role = iota + Minimize Role = iota + Zoom Role = iota + FullScreen Role = iota //Front Role = iota //WindowRole Role = iota @@ -132,6 +133,12 @@ func newWindowMenu() *MenuItem { menu := NewMenu() menu.AddRole(Minimize) menu.AddRole(Zoom) + if runtime.GOOS == "darwin" { + menu.AddSeparator() + menu.AddRole(FullScreen) + } else { + menu.AddRole(Close) + } subMenu := newSubMenuItem("Window") subMenu.submenu = menu return subMenu diff --git a/v3/pkg/application/screen_darwin.go b/v3/pkg/application/screen_darwin.go index d45753f7b..0669d531a 100644 --- a/v3/pkg/application/screen_darwin.go +++ b/v3/pkg/application/screen_darwin.go @@ -128,12 +128,12 @@ func cScreenToScreen(screen C.Screen) *Screen { } } -func getPrimaryScreen() (*Screen, error) { +func (m *macosApp) getPrimaryScreen() (*Screen, error) { cScreen := C.GetPrimaryScreen() return cScreenToScreen(cScreen), nil } -func getScreens() ([]*Screen, error) { +func (m *macosApp) getScreens() ([]*Screen, error) { cScreens := C.getAllScreens() defer C.free(unsafe.Pointer(cScreens)) numScreens := int(C.GetNumScreens()) diff --git a/v3/pkg/application/webview_window.go b/v3/pkg/application/webview_window.go index e9899fe82..1537cd8b3 100644 --- a/v3/pkg/application/webview_window.go +++ b/v3/pkg/application/webview_window.go @@ -1,7 +1,9 @@ package application import ( + "errors" "fmt" + "github.com/samber/lo" "sync" "time" @@ -20,8 +22,7 @@ type ( setMinSize(width, height int) setMaxSize(width, height int) execJS(js string) - restore() - setBackgroundColour(color *RGBA) + setBackgroundColour(color RGBA) run() center() size() (int, int) @@ -51,27 +52,37 @@ type ( isMinimised() bool isMaximised() bool isFullscreen() bool - disableSizeConstraints() + isNormal() bool + isVisible() bool setFullscreenButtonEnabled(enabled bool) show() hide() getScreen() (*Screen, error) setFrameless(bool) openContextMenu(menu *Menu, data *ContextMenuData) + nativeWindowHandle() uintptr } ) +type WindowEventListener struct { + callback func(ctx *WindowEventContext) +} + type WebviewWindow struct { - options *WebviewWindowOptions + options WebviewWindowOptions impl webviewWindowImpl implLock sync.RWMutex id uint - eventListeners map[uint][]func(ctx *WindowEventContext) + eventListeners map[uint][]*WindowEventListener eventListenersLock sync.RWMutex contextMenus map[string]*Menu contextMenusLock sync.RWMutex + + // A map of listener cancellation functions + cancellersLock sync.RWMutex + cancellers []func() } var windowID uint @@ -84,7 +95,15 @@ func getWindowID() uint { return windowID } -func NewWindow(options *WebviewWindowOptions) *WebviewWindow { +// Use onApplicationEvent to register a callback for an application event from a window. +// This will handle tidying up the callback when the window is destroyed +func (w *WebviewWindow) onApplicationEvent(eventType events.ApplicationEventType, callback func()) { + cancelFn := globalApplication.On(eventType, callback) + w.addCancellationFunction(cancelFn) +} + +// NewWindow creates a new window with the given options +func NewWindow(options WebviewWindowOptions) *WebviewWindow { if options.Width == 0 { options.Width = 800 } @@ -98,27 +117,38 @@ func NewWindow(options *WebviewWindowOptions) *WebviewWindow { result := &WebviewWindow{ id: getWindowID(), options: options, - eventListeners: make(map[uint][]func(ctx *WindowEventContext)), + eventListeners: make(map[uint][]*WindowEventListener), contextMenus: make(map[string]*Menu), } return result } +func (w *WebviewWindow) addCancellationFunction(canceller func()) { + w.cancellersLock.Lock() + defer w.cancellersLock.Unlock() + w.cancellers = append(w.cancellers, canceller) +} + +// SetTitle sets the title of the window func (w *WebviewWindow) SetTitle(title string) *WebviewWindow { w.implLock.RLock() defer w.implLock.RUnlock() w.options.Title = title if w.impl != nil { - w.impl.setTitle(title) + invokeSync(func() { + w.impl.setTitle(title) + }) } return w } +// Name returns the name of the window func (w *WebviewWindow) Name() string { return w.options.Name } +// SetSize sets the size of the window func (w *WebviewWindow) SetSize(width, height int) *WebviewWindow { // Don't set size if fullscreen if w.IsFullscreen() { @@ -154,7 +184,9 @@ func (w *WebviewWindow) SetSize(width, height int) *WebviewWindow { } if w.impl != nil { - w.impl.setSize(width, height) + invokeSync(func() { + w.impl.setSize(width, height) + }) } return w } @@ -166,17 +198,21 @@ func (w *WebviewWindow) run() { w.implLock.Lock() w.impl = newWindowImpl(w) w.implLock.Unlock() - w.impl.run() + invokeSync(w.impl.run) } +// SetAlwaysOnTop sets the window to be always on top. func (w *WebviewWindow) SetAlwaysOnTop(b bool) *WebviewWindow { w.options.AlwaysOnTop = b - if w.impl == nil { - w.impl.setAlwaysOnTop(b) + if w.impl != nil { + invokeSync(func() { + w.impl.setAlwaysOnTop(b) + }) } return w } +// Show shows the window. func (w *WebviewWindow) Show() *WebviewWindow { if globalApplication.impl == nil { return w @@ -185,13 +221,15 @@ func (w *WebviewWindow) Show() *WebviewWindow { w.run() return w } - w.impl.show() + invokeSync(w.impl.show) return w } + +// Hide hides the window. func (w *WebviewWindow) Hide() *WebviewWindow { w.options.Hidden = true if w.impl != nil { - w.impl.hide() + invokeSync(w.impl.hide) } return w } @@ -199,38 +237,49 @@ func (w *WebviewWindow) Hide() *WebviewWindow { func (w *WebviewWindow) SetURL(s string) *WebviewWindow { w.options.URL = s if w.impl != nil { - w.impl.setURL(s) + invokeSync(func() { + w.impl.setURL(s) + }) } return w } +// SetZoom sets the zoom level of the window. func (w *WebviewWindow) SetZoom(magnification float64) *WebviewWindow { w.options.Zoom = magnification if w.impl != nil { - w.impl.setZoom(magnification) + invokeSync(func() { + w.impl.setZoom(magnification) + }) } return w } +// GetZoom returns the current zoom level of the window. func (w *WebviewWindow) GetZoom() float64 { if w.impl != nil { - return w.impl.getZoom() + return invokeSyncWithResult(w.impl.getZoom) } return 1 } +// SetResizable sets whether the window is resizable. func (w *WebviewWindow) SetResizable(b bool) *WebviewWindow { w.options.DisableResize = !b if w.impl != nil { - w.impl.setResizable(b) + invokeSync(func() { + w.impl.setResizable(b) + }) } return w } +// Resizable returns true if the window is resizable. func (w *WebviewWindow) Resizable() bool { return !w.options.DisableResize } +// SetMinSize sets the minimum size of the window. func (w *WebviewWindow) SetMinSize(minWidth, minHeight int) *WebviewWindow { w.options.MinWidth = minWidth w.options.MinHeight = minHeight @@ -251,13 +300,18 @@ func (w *WebviewWindow) SetMinSize(minWidth, minHeight int) *WebviewWindow { } if w.impl != nil { if newSize { - w.impl.setSize(newWidth, newHeight) + invokeSync(func() { + w.impl.setSize(newWidth, newHeight) + }) } - w.impl.setMinSize(minWidth, minHeight) + invokeSync(func() { + w.impl.setMinSize(minWidth, minHeight) + }) } return w } +// SetMaxSize sets the maximum size of the window. func (w *WebviewWindow) SetMaxSize(maxWidth, maxHeight int) *WebviewWindow { w.options.MaxWidth = maxWidth w.options.MaxHeight = maxHeight @@ -278,13 +332,18 @@ func (w *WebviewWindow) SetMaxSize(maxWidth, maxHeight int) *WebviewWindow { } if w.impl != nil { if newSize { - w.impl.setSize(newWidth, newHeight) + invokeSync(func() { + w.impl.setSize(newWidth, newHeight) + }) } - w.impl.setMaxSize(maxWidth, maxHeight) + invokeSync(func() { + w.impl.setMaxSize(maxWidth, maxHeight) + }) } return w } +// ExecJS executes the given javascript in the context of the window. func (w *WebviewWindow) ExecJS(js string) { if w.impl == nil { return @@ -292,6 +351,7 @@ func (w *WebviewWindow) ExecJS(js string) { w.impl.execJS(js) } +// Fullscreen sets the window to fullscreen mode. Min/Max size constraints are disabled. func (w *WebviewWindow) Fullscreen() *WebviewWindow { if w.impl == nil { w.options.StartState = WindowStateFullscreen @@ -299,7 +359,7 @@ func (w *WebviewWindow) Fullscreen() *WebviewWindow { } if !w.IsFullscreen() { w.disableSizeConstraints() - w.impl.fullscreen() + invokeSync(w.impl.fullscreen) } return w } @@ -307,7 +367,9 @@ func (w *WebviewWindow) Fullscreen() *WebviewWindow { func (w *WebviewWindow) SetFullscreenButtonEnabled(enabled bool) *WebviewWindow { w.options.FullscreenButtonEnabled = enabled if w.impl != nil { - w.impl.setFullscreenButtonEnabled(enabled) + invokeSync(func() { + w.impl.setFullscreenButtonEnabled(enabled) + }) } return w } @@ -317,7 +379,15 @@ func (w *WebviewWindow) IsMinimised() bool { if w.impl == nil { return false } - return w.impl.isMinimised() + return invokeSyncWithResult(w.impl.isMinimised) +} + +// IsVisible returns true if the window is visible +func (w *WebviewWindow) IsVisible() bool { + if w.impl == nil { + return false + } + return invokeSyncWithResult(w.impl.isVisible) } // IsMaximised returns true if the window is maximised @@ -325,15 +395,19 @@ func (w *WebviewWindow) IsMaximised() bool { if w.impl == nil { return false } - return w.impl.isMaximised() + return invokeSyncWithResult(w.impl.isMaximised) } // Size returns the size of the window -func (w *WebviewWindow) Size() (width int, height int) { +func (w *WebviewWindow) Size() (int, int) { if w.impl == nil { return 0, 0 } - return w.impl.size() + var width, height int + invokeSync(func() { + width, height = w.impl.size() + }) + return width, height } // IsFullscreen returns true if the window is fullscreen @@ -343,13 +417,16 @@ func (w *WebviewWindow) IsFullscreen() bool { if w.impl == nil { return false } - return w.impl.isFullscreen() + return invokeSyncWithResult(w.impl.isFullscreen) } -func (w *WebviewWindow) SetBackgroundColour(colour *RGBA) *WebviewWindow { +// SetBackgroundColour sets the background colour of the window +func (w *WebviewWindow) SetBackgroundColour(colour RGBA) *WebviewWindow { w.options.BackgroundColour = colour if w.impl != nil { - w.impl.setBackgroundColour(colour) + invokeSync(func() { + w.impl.setBackgroundColour(colour) + }) } return w } @@ -358,170 +435,205 @@ func (w *WebviewWindow) handleMessage(message string) { w.info(message) // Check for special messages if message == "test" { - w.SetTitle("Hello World") + invokeSync(func() { + w.SetTitle("Hello World") + }) } w.info("ProcessMessage from front end:", message) } +// Center centers the window on the screen func (w *WebviewWindow) Center() { if w.impl == nil { return } - w.impl.center() + invokeSync(w.impl.center) } -func (w *WebviewWindow) On(eventType events.WindowEventType, callback func(ctx *WindowEventContext)) { +// On registers a callback for the given window event +func (w *WebviewWindow) On(eventType events.WindowEventType, callback func(ctx *WindowEventContext)) func() { eventID := uint(eventType) w.eventListenersLock.Lock() defer w.eventListenersLock.Unlock() - w.eventListeners[eventID] = append(w.eventListeners[eventID], callback) + windowEventListener := &WindowEventListener{ + callback: callback, + } + w.eventListeners[eventID] = append(w.eventListeners[eventID], windowEventListener) if w.impl != nil { w.impl.on(eventID) } + + return func() { + w.eventListenersLock.Lock() + defer w.eventListenersLock.Unlock() + w.eventListeners[eventID] = lo.Without(w.eventListeners[eventID], windowEventListener) + } + } func (w *WebviewWindow) handleWindowEvent(id uint) { w.eventListenersLock.RLock() - for _, callback := range w.eventListeners[id] { - go callback(blankWindowEventContext) + for _, listener := range w.eventListeners[id] { + go listener.callback(blankWindowEventContext) } w.eventListenersLock.RUnlock() } +// Width returns the width of the window func (w *WebviewWindow) Width() int { if w.impl == nil { return 0 } - return w.impl.width() + return invokeSyncWithResult(w.impl.width) } +// Height returns the height of the window func (w *WebviewWindow) Height() int { if w.impl == nil { return 0 } - return w.impl.height() + return invokeSyncWithResult(w.impl.height) } +// Position returns the position of the window func (w *WebviewWindow) Position() (int, int) { w.implLock.RLock() defer w.implLock.RUnlock() if w.impl == nil { return 0, 0 } - return w.impl.position() + var x, y int + invokeSync(func() { + x, y = w.impl.position() + }) + return x, y } func (w *WebviewWindow) Destroy() { if w.impl == nil { return } - w.impl.destroy() + // Cancel the callbacks + for _, cancelFunc := range w.cancellers { + cancelFunc() + } + invokeSync(w.impl.destroy) } +// Reload reloads the page assets func (w *WebviewWindow) Reload() { if w.impl == nil { return } - w.impl.reload() + invokeSync(w.impl.reload) } +// ForceReload forces the window to reload the page assets func (w *WebviewWindow) ForceReload() { if w.impl == nil { return } - w.impl.forceReload() + invokeSync(w.impl.forceReload) } +// ToggleFullscreen toggles the window between fullscreen and normal func (w *WebviewWindow) ToggleFullscreen() { if w.impl == nil { return } - if w.IsFullscreen() { - w.UnFullscreen() - } else { - w.Fullscreen() - } + invokeSync(func() { + if w.IsFullscreen() { + w.UnFullscreen() + } else { + w.Fullscreen() + } + }) } func (w *WebviewWindow) ToggleDevTools() { if w.impl == nil { return } - w.impl.toggleDevTools() + invokeSync(w.impl.toggleDevTools) } +// ZoomReset resets the zoom level of the webview content to 100% func (w *WebviewWindow) ZoomReset() *WebviewWindow { if w.impl != nil { - w.impl.zoomReset() + invokeSync(w.impl.zoomReset) } return w } +// ZoomIn increases the zoom level of the webview content func (w *WebviewWindow) ZoomIn() { if w.impl == nil { return } - w.impl.zoomIn() + invokeSync(w.impl.zoomIn) } +// ZoomOut decreases the zoom level of the webview content func (w *WebviewWindow) ZoomOut() { if w.impl == nil { return } - w.impl.zoomOut() + invokeSync(w.impl.zoomOut) } +// Close closes the window func (w *WebviewWindow) Close() { if w.impl == nil { return } - w.impl.close() -} - -func (w *WebviewWindow) Minimize() { - if w.impl == nil { - return - } - w.impl.minimise() + invokeSync(w.impl.close) } func (w *WebviewWindow) Zoom() { if w.impl == nil { return } - w.impl.zoom() + invokeSync(w.impl.zoom) } +// SetHTML sets the HTML of the window to the given html string. func (w *WebviewWindow) SetHTML(html string) *WebviewWindow { w.options.HTML = html if w.impl != nil { - w.impl.setHTML(html) + invokeSync(func() { + w.impl.setHTML(html) + }) } return w } +// SetPosition sets the position of the window. func (w *WebviewWindow) SetPosition(x, y int) *WebviewWindow { w.options.X = x w.options.Y = y if w.impl != nil { - w.impl.setPosition(x, y) + invokeSync(func() { + w.impl.setPosition(x, y) + }) } return w } +// Minimise minimises the window. func (w *WebviewWindow) Minimise() *WebviewWindow { if w.impl == nil { w.options.StartState = WindowStateMinimised return w } if !w.IsMinimised() { - w.impl.minimise() + invokeSync(w.impl.minimise) } return w } +// Maximise maximises the window. Min/Max size constraints are disabled. func (w *WebviewWindow) Maximise() *WebviewWindow { if w.impl == nil { w.options.StartState = WindowStateMaximised @@ -529,74 +641,102 @@ func (w *WebviewWindow) Maximise() *WebviewWindow { } if !w.IsMaximised() { w.disableSizeConstraints() - w.impl.maximise() + invokeSync(w.impl.maximise) } return w } +// UnMinimise un-minimises the window. Min/Max size constraints are re-enabled. func (w *WebviewWindow) UnMinimise() { if w.impl == nil { return } - w.impl.unminimise() + if w.IsMinimised() { + invokeSync(w.impl.unminimise) + } } +// UnMaximise un-maximises the window. func (w *WebviewWindow) UnMaximise() { if w.impl == nil { return } - w.enableSizeConstraints() - w.impl.unmaximise() + if w.IsMaximised() { + w.enableSizeConstraints() + invokeSync(w.impl.unmaximise) + } } +// UnFullscreen un-fullscreens the window. func (w *WebviewWindow) UnFullscreen() { if w.impl == nil { return } - w.enableSizeConstraints() - w.impl.unfullscreen() + if w.IsFullscreen() { + w.enableSizeConstraints() + invokeSync(w.impl.unfullscreen) + } } +// Restore restores the window to its previous state if it was previously minimised, maximised or fullscreen. func (w *WebviewWindow) Restore() { if w.impl == nil { return } - if w.IsMinimised() { - w.UnMinimise() - } else if w.IsMaximised() { - w.UnMaximise() - } else if w.IsFullscreen() { - w.UnFullscreen() - } + invokeSync(func() { + if w.IsMinimised() { + w.UnMinimise() + } else if w.IsMaximised() { + w.UnMaximise() + } else if w.IsFullscreen() { + w.UnFullscreen() + } + }) } func (w *WebviewWindow) disableSizeConstraints() { if w.impl == nil { return } - w.impl.setMinSize(0, 0) - w.impl.setMaxSize(0, 0) + invokeSync(func() { + if w.options.MinWidth > 0 && w.options.MinHeight > 0 { + w.impl.setMinSize(0, 0) + } + if w.options.MaxWidth > 0 && w.options.MaxHeight > 0 { + w.impl.setMaxSize(0, 0) + } + }) } func (w *WebviewWindow) enableSizeConstraints() { if w.impl == nil { return } - w.SetMinSize(w.options.MinWidth, w.options.MinHeight) - w.SetMaxSize(w.options.MaxWidth, w.options.MaxHeight) + invokeSync(func() { + if w.options.MinWidth > 0 && w.options.MinHeight > 0 { + w.SetMinSize(w.options.MinWidth, w.options.MinHeight) + } + if w.options.MaxWidth > 0 && w.options.MaxHeight > 0 { + w.SetMaxSize(w.options.MaxWidth, w.options.MaxHeight) + } + }) } +// GetScreen returns the screen that the window is on func (w *WebviewWindow) GetScreen() (*Screen, error) { if w.impl == nil { return nil, nil } - return w.impl.getScreen() + return invokeSyncWithResultAndError(w.impl.getScreen) } +// SetFrameless removes the window frame and title bar func (w *WebviewWindow) SetFrameless(frameless bool) *WebviewWindow { w.options.Frameless = frameless if w.impl != nil { - w.impl.setFrameless(frameless) + invokeSync(func() { + w.impl.setFrameless(frameless) + }) } return w } @@ -631,7 +771,7 @@ func (w *WebviewWindow) handleDragAndDropMessage(event *dragAndDropMessage) { ctx := newWindowEventContext() ctx.setDroppedFiles(event.filenames) for _, listener := range w.eventListeners[uint(events.FilesDropped)] { - listener(ctx) + listener.callback(ctx) } } @@ -652,8 +792,17 @@ func (w *WebviewWindow) openContextMenu(data *ContextMenuData) { w.impl.openContextMenu(menu, data) } +// RegisterContextMenu registers a context menu and assigns it the given name. func (w *WebviewWindow) RegisterContextMenu(name string, menu *Menu) { w.contextMenusLock.Lock() defer w.contextMenusLock.Unlock() w.contextMenus[name] = menu } + +// NativeWindowHandle returns the platform native window handle for the window. +func (w *WebviewWindow) NativeWindowHandle() (uintptr, error) { + if w.impl == nil { + return 0, errors.New("native handle unavailable as window is not running") + } + return w.impl.nativeWindowHandle(), nil +} diff --git a/v3/pkg/application/webview_window_darwin.go b/v3/pkg/application/webview_window_darwin.go index c8728f2e9..60305c49a 100644 --- a/v3/pkg/application/webview_window_darwin.go +++ b/v3/pkg/application/webview_window_darwin.go @@ -703,6 +703,12 @@ static bool isFullScreen(void *window) { return (mask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen; } +static bool isVisible(void *window) { + // get main window + WebviewWindow* nsWindow = (WebviewWindow*)window; + return (nsWindow.occlusionState & NSWindowOcclusionStateVisible) == NSWindowOcclusionStateVisible; +} + // windowSetFullScreen static void windowSetFullScreen(void *window, bool fullscreen) { if (isFullScreen(window)) { @@ -942,6 +948,14 @@ func (w *macosWebviewWindow) isFullscreen() bool { }) } +func (w *macosWebviewWindow) isNormal() bool { + return !w.isMinimised() && !w.isMaximised() && !w.isFullscreen() +} + +func (w *macosWebviewWindow) isVisible() bool { + return bool(C.isVisible(w.nsWindow)) +} + func (w *macosWebviewWindow) syncMainThreadReturningBool(fn func() bool) bool { var wg sync.WaitGroup wg.Add(1) @@ -1147,10 +1161,12 @@ func (w *macosWebviewWindow) run() { }) } -func (w *macosWebviewWindow) setBackgroundColour(colour *RGBA) { - if colour == nil { - return - } +func (w *macosWebviewWindow) nativeWindowHandle() uintptr { + return uintptr(w.nsWindow) +} + +func (w *macosWebviewWindow) setBackgroundColour(colour RGBA) { + C.windowSetBackgroundColour(w.nsWindow, C.int(colour.Red), C.int(colour.Green), C.int(colour.Blue), C.int(colour.Alpha)) } diff --git a/v3/pkg/application/webview_window_windows.go b/v3/pkg/application/webview_window_windows.go index f651a7739..6d51a3f72 100644 --- a/v3/pkg/application/webview_window_windows.go +++ b/v3/pkg/application/webview_window_windows.go @@ -3,7 +3,14 @@ package application import ( + "errors" + "fmt" + "strconv" + "unicode/utf16" "unsafe" + + "github.com/wailsapp/wails/v3/pkg/events" + "github.com/wailsapp/wails/v3/pkg/w32" ) var showDevTools = func(window unsafe.Pointer) {} @@ -11,21 +18,35 @@ var showDevTools = func(window unsafe.Pointer) {} type windowsWebviewWindow struct { windowImpl unsafe.Pointer parent *WebviewWindow + hwnd w32.HWND + + // Fullscreen flags + isCurrentlyFullscreen bool + previousWindowStyle uint32 + previousWindowExStyle uint32 + previousWindowPlacement w32.WINDOWPLACEMENT +} + +func (w *windowsWebviewWindow) nativeWindowHandle() uintptr { + return w.hwnd } func (w *windowsWebviewWindow) setTitle(title string) { - //TODO implement me - panic("implement me") + w32.SetWindowText(w.hwnd, title) } func (w *windowsWebviewWindow) setSize(width, height int) { - //TODO implement me - panic("implement me") + rect := w32.GetWindowRect(w.hwnd) + width, height = w.scaleWithWindowDPI(width, height) + w32.MoveWindow(w.hwnd, int(rect.Left), int(rect.Top), width, height, true) } func (w *windowsWebviewWindow) setAlwaysOnTop(alwaysOnTop bool) { - //TODO implement me - panic("implement me") + position := w32.HWND_NOTOPMOST + if alwaysOnTop { + position = w32.HWND_TOPMOST + } + w32.SetWindowPos(w.hwnd, position, 0, 0, 0, 0, uint(w32.SWP_NOMOVE|w32.SWP_NOSIZE)) } func (w *windowsWebviewWindow) setURL(url string) { @@ -34,18 +55,17 @@ func (w *windowsWebviewWindow) setURL(url string) { } func (w *windowsWebviewWindow) setResizable(resizable bool) { - //TODO implement me - panic("implement me") + w.setStyle(resizable, w32.WS_THICKFRAME) } func (w *windowsWebviewWindow) setMinSize(width, height int) { - //TODO implement me - panic("implement me") + w.parent.options.MinWidth = width + w.parent.options.MinHeight = height } func (w *windowsWebviewWindow) setMaxSize(width, height int) { - //TODO implement me - panic("implement me") + w.parent.options.MaxWidth = width + w.parent.options.MaxHeight = height } func (w *windowsWebviewWindow) execJS(js string) { @@ -53,44 +73,167 @@ func (w *windowsWebviewWindow) execJS(js string) { panic("implement me") } -func (w *windowsWebviewWindow) restore() { - //TODO implement me - panic("implement me") +func (w *windowsWebviewWindow) setBackgroundColour(color RGBA) { + w32.SetBackgroundColour(w.hwnd, color.Red, color.Green, color.Blue) } -func (w *windowsWebviewWindow) setBackgroundColour(color *RGBA) { - //TODO implement me - panic("implement me") +func (w *windowsWebviewWindow) framelessWithDecorations() bool { + return w.parent.options.Frameless && !w.parent.options.Windows.DisableFramelessWindowDecorations } func (w *windowsWebviewWindow) run() { - //TODO implement me - panic("implement me") + + options := w.parent.options + + var exStyle uint + exStyle = w32.WS_EX_CONTROLPARENT | w32.WS_EX_APPWINDOW + if options.BackgroundType != BackgroundTypeSolid { + exStyle |= w32.WS_EX_NOREDIRECTIONBITMAP + } + if options.AlwaysOnTop { + exStyle |= w32.WS_EX_TOPMOST + } + w.hwnd = w32.CreateWindowEx( + exStyle, + windowClassName, + w32.MustStringToUTF16Ptr(options.Title), + w32.WS_OVERLAPPEDWINDOW, + w32.CW_USEDEFAULT, + w32.CW_USEDEFAULT, + options.Width, + options.Height, + 0, + 0, + w32.GetModuleHandle(""), + nil) + + if w.hwnd == 0 { + panic("Unable to create window") + } + + // Register the window with the application + windowsApp := globalApplication.impl.(*windowsApp) + windowsApp.registerWindow(w) + + w.setResizable(!options.DisableResize) + + if options.Frameless { + // Inform the application of the frame change this is needed to trigger the WM_NCCALCSIZE event. + // => https://learn.microsoft.com/en-us/windows/win32/dwm/customframe#removing-the-standard-frame + // This is normally done in WM_CREATE but we can't handle that there because that is emitted during CreateWindowEx + // and at that time we can't yet register the window for calling our WndProc method. + // This must be called after setResizable above! + rcClient := w32.GetWindowRect(w.hwnd) + w32.SetWindowPos(w.hwnd, + 0, + int(rcClient.Left), + int(rcClient.Top), + int(rcClient.Right-rcClient.Left), + int(rcClient.Bottom-rcClient.Top), + w32.SWP_FRAMECHANGED) + } + + // Icon + if !options.Windows.DisableIcon { + // App icon ID is 3 + icon, err := NewIconFromResource(w32.GetModuleHandle(""), uint16(3)) + if err == nil { + w.setIcon(icon) + } + } else { + w.disableIcon() + } + + switch options.BackgroundType { + case BackgroundTypeSolid: + w.setBackgroundColour(options.BackgroundColour) + case BackgroundTypeTransparent: + case BackgroundTypeTranslucent: + w.setBackdropType(options.Windows.BackdropType) + } + + // Process the theme + switch options.Windows.Theme { + case SystemDefault: + w.updateTheme(w32.IsCurrentlyDarkMode()) + w.parent.onApplicationEvent(events.Windows.SystemThemeChanged, func() { + w.updateTheme(w32.IsCurrentlyDarkMode()) + }) + case Light: + w.updateTheme(false) + case Dark: + w.updateTheme(true) + } + + // Process StartState + switch options.StartState { + case WindowStateMaximised: + if w.parent.Resizable() { + w.maximise() + } + case WindowStateMinimised: + w.minimise() + case WindowStateFullscreen: + w.fullscreen() + } + + w.setForeground() + + if !options.Hidden { + w.show() + w.update() + } } func (w *windowsWebviewWindow) center() { - //TODO implement me - panic("implement me") + w32.CenterWindow(w.hwnd) +} + +func (w *windowsWebviewWindow) disableSizeConstraints() { + w.setMaxSize(0, 0) + w.setMinSize(0, 0) +} + +func (w *windowsWebviewWindow) enableSizeConstraints() { + options := w.parent.options + if options.MinWidth > 0 || options.MinHeight > 0 { + w.setMinSize(options.MinWidth, options.MinHeight) + } + if options.MaxWidth > 0 || options.MaxHeight > 0 { + w.setMaxSize(options.MaxWidth, options.MaxHeight) + } } func (w *windowsWebviewWindow) size() (int, int) { - //TODO implement me - panic("implement me") + rect := w32.GetWindowRect(w.hwnd) + width := int(rect.Right - rect.Left) + height := int(rect.Bottom - rect.Top) + width, height = w.scaleToDefaultDPI(width, height) + return width, height +} + +func (w *windowsWebviewWindow) setForeground() { + w32.SetForegroundWindow(w.hwnd) +} + +func (w *windowsWebviewWindow) update() { + w32.UpdateWindow(w.hwnd) } func (w *windowsWebviewWindow) width() int { - //TODO implement me - panic("implement me") + width, _ := w.size() + return width } func (w *windowsWebviewWindow) height() int { - //TODO implement me - panic("implement me") + _, height := w.size() + return height } func (w *windowsWebviewWindow) position() (int, int) { - //TODO implement me - panic("implement me") + rect := w32.GetWindowRect(w.hwnd) + left, right := w.scaleToDefaultDPI(int(rect.Left), int(rect.Right)) + return left, right } func (w *windowsWebviewWindow) destroy() { @@ -154,83 +297,181 @@ func (w *windowsWebviewWindow) setHTML(html string) { } func (w *windowsWebviewWindow) setPosition(x int, y int) { - //TODO implement me - panic("implement me") + x, y = w.scaleWithWindowDPI(x, y) + info := w32.GetMonitorInfoForWindow(w.hwnd) + workRect := info.RcWork + w32.SetWindowPos(w.hwnd, w32.HWND_TOP, int(workRect.Left)+x, int(workRect.Top)+y, 0, 0, w32.SWP_NOSIZE) } +// on is used to indicate that a particular event should be listened for func (w *windowsWebviewWindow) on(eventID uint) { //TODO implement me panic("implement me") } func (w *windowsWebviewWindow) minimise() { - //TODO implement me - panic("implement me") + w32.ShowWindow(w.hwnd, w32.SW_MINIMIZE) } func (w *windowsWebviewWindow) unminimise() { - //TODO implement me - panic("implement me") + w.restore() } func (w *windowsWebviewWindow) maximise() { - //TODO implement me - panic("implement me") + w32.ShowWindow(w.hwnd, w32.SW_MAXIMIZE) } func (w *windowsWebviewWindow) unmaximise() { - //TODO implement me - panic("implement me") + w.restore() +} + +func (w *windowsWebviewWindow) restore() { + w32.ShowWindow(w.hwnd, w32.SW_RESTORE) } func (w *windowsWebviewWindow) fullscreen() { - //TODO implement me - panic("implement me") + if w.isFullscreen() { + return + } + if w.framelessWithDecorations() { + w32.ExtendFrameIntoClientArea(w.hwnd, false) + } + w.disableSizeConstraints() + w.previousWindowStyle = uint32(w32.GetWindowLongPtr(w.hwnd, w32.GWL_STYLE)) + w.previousWindowExStyle = uint32(w32.GetWindowLong(w.hwnd, w32.GWL_EXSTYLE)) + monitor := w32.MonitorFromWindow(w.hwnd, w32.MONITOR_DEFAULTTOPRIMARY) + var monitorInfo w32.MONITORINFO + monitorInfo.CbSize = uint32(unsafe.Sizeof(monitorInfo)) + if !w32.GetMonitorInfo(monitor, &monitorInfo) { + return + } + if !w32.GetWindowPlacement(w.hwnd, &w.previousWindowPlacement) { + return + } + // According to https://devblogs.microsoft.com/oldnewthing/20050505-04/?p=35703 one should use w32.WS_POPUP | w32.WS_VISIBLE + w32.SetWindowLong(w.hwnd, w32.GWL_STYLE, w.previousWindowStyle & ^uint32(w32.WS_OVERLAPPEDWINDOW) | (w32.WS_POPUP|w32.WS_VISIBLE)) + w32.SetWindowLong(w.hwnd, w32.GWL_EXSTYLE, w.previousWindowExStyle & ^uint32(w32.WS_EX_DLGMODALFRAME)) + w.isCurrentlyFullscreen = true + w32.SetWindowPos(w.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) } func (w *windowsWebviewWindow) unfullscreen() { - //TODO implement me - panic("implement me") + if !w.isFullscreen() { + return + } + if w.framelessWithDecorations() { + w32.ExtendFrameIntoClientArea(w.hwnd, true) + } + w32.SetWindowLong(w.hwnd, w32.GWL_STYLE, w.previousWindowStyle) + w32.SetWindowLong(w.hwnd, w32.GWL_EXSTYLE, w.previousWindowExStyle) + w32.SetWindowPlacement(w.hwnd, &w.previousWindowPlacement) + w.isCurrentlyFullscreen = false + w32.SetWindowPos(w.hwnd, 0, 0, 0, 0, 0, + w32.SWP_NOMOVE|w32.SWP_NOSIZE|w32.SWP_NOZORDER|w32.SWP_NOOWNERZORDER|w32.SWP_FRAMECHANGED) + w.enableSizeConstraints() } func (w *windowsWebviewWindow) isMinimised() bool { - //TODO implement me - panic("implement me") + style := uint32(w32.GetWindowLong(w.hwnd, w32.GWL_STYLE)) + return style&w32.WS_MINIMIZE != 0 } func (w *windowsWebviewWindow) isMaximised() bool { - //TODO implement me - panic("implement me") + style := uint32(w32.GetWindowLong(w.hwnd, w32.GWL_STYLE)) + return style&w32.WS_MAXIMIZE != 0 } func (w *windowsWebviewWindow) isFullscreen() bool { - //TODO implement me - panic("implement me") + // TODO: Actually calculate this based on size of window against screen size + // => stffabi: This flag is essential since it indicates that we are in fullscreen mode even before the native properties + // reflect this, e.g. when needing to know if we are in fullscreen during a wndproc message. + // That's also why this flag is set before SetWindowPos in v2 in fullscreen/unfullscreen. + return w.isCurrentlyFullscreen } -func (w *windowsWebviewWindow) disableSizeConstraints() { - //TODO implement me - panic("implement me") +func (w *windowsWebviewWindow) isNormal() bool { + return !w.isMinimised() && !w.isMaximised() && !w.isFullscreen() } -func (w *windowsWebviewWindow) setFullscreenButtonEnabled(enabled bool) { - //TODO implement me - panic("implement me") +func (w *windowsWebviewWindow) isVisible() bool { + style := uint32(w32.GetWindowLong(w.hwnd, w32.GWL_STYLE)) + return style&w32.WS_VISIBLE != 0 +} + +func (w *windowsWebviewWindow) setFullscreenButtonEnabled(_ bool) { + // Unused in Windows } func (w *windowsWebviewWindow) show() { - //TODO implement me - panic("implement me") + w32.ShowWindow(w.hwnd, w32.SW_SHOW) } func (w *windowsWebviewWindow) hide() { - //TODO implement me - panic("implement me") + w32.ShowWindow(w.hwnd, w32.SW_HIDE) } +// Get the screen for the current window func (w *windowsWebviewWindow) getScreen() (*Screen, error) { - //TODO implement me - panic("implement me") + hMonitor := w32.MonitorFromWindow(w.hwnd, w32.MONITOR_DEFAULTTONEAREST) + + var mi w32.MONITORINFOEX + mi.CbSize = uint32(unsafe.Sizeof(mi)) + w32.GetMonitorInfoEx(hMonitor, &mi) + var thisScreen Screen + thisScreen.X = int(mi.RcMonitor.Left) + thisScreen.Y = int(mi.RcMonitor.Top) + thisScreen.Size = Size{ + Width: int(mi.RcMonitor.Right - mi.RcMonitor.Left), + Height: int(mi.RcMonitor.Bottom - mi.RcMonitor.Top), + } + thisScreen.Bounds = Rect{ + X: int(mi.RcMonitor.Left), + Y: int(mi.RcMonitor.Top), + Width: int(mi.RcMonitor.Right - mi.RcMonitor.Left), + Height: int(mi.RcMonitor.Bottom - mi.RcMonitor.Top), + } + thisScreen.WorkArea = Rect{ + X: int(mi.RcWork.Left), + Y: int(mi.RcWork.Top), + Width: int(mi.RcWork.Right - mi.RcWork.Left), + Height: int(mi.RcWork.Bottom - mi.RcWork.Top), + } + thisScreen.ID = strconv.Itoa(int(hMonitor)) + thisScreen.Name = string(utf16.Decode(mi.SzDevice[:])) + var xdpi, ydpi w32.UINT + w32.GetDPIForMonitor(hMonitor, w32.MDT_EFFECTIVE_DPI, &xdpi, &ydpi) + thisScreen.Scale = float32(xdpi) / 96.0 + thisScreen.IsPrimary = mi.DwFlags&w32.MONITORINFOF_PRIMARY != 0 + + // TODO: Get screen rotation + // https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-devmodea + + //// get display settings for monitor + //var dm w32.DEVMODE + //dm.DmSize = uint16(unsafe.Sizeof(dm)) + //dm.DmDriverExtra = 0 + //w32.EnumDisplaySettingsEx(&mi.SzDevice[0], w32.ENUM_CURRENT_SETTINGS, &dm, 0) + // + //// check display settings for rotation + //rotationAngle := dm.DmDi + //if rotationAngle == DMDO_0 { + // printf("Monitor is not rotated\n") + //} else if rotationAngle == DMDO_90 { + // printf("Monitor is rotated 90 degrees\n") + //} else if rotationAngle == DMDO_180 { + // printf("Monitor is rotated 180 degrees\n") + //} else if rotationAngle == DMDO_270 { + // printf("Monitor is rotated 270 degrees\n") + //} else { + // printf("Monitor is rotated at an unknown angle\n") + //} + + return &thisScreen, nil } func (w *windowsWebviewWindow) setFrameless(b bool) { @@ -242,6 +483,7 @@ func newWindowImpl(parent *WebviewWindow) *windowsWebviewWindow { result := &windowsWebviewWindow{ parent: parent, } + return result } @@ -251,3 +493,339 @@ func (w *windowsWebviewWindow) openContextMenu(menu *Menu, data *ContextMenuData thisMenu.update() //C.windowShowMenu(w.nsWindow, thisMenu.nsMenu, C.int(data.X), C.int(data.Y)) } + +func (w *windowsWebviewWindow) setStyle(b bool, style int) { + currentStyle := int(w32.GetWindowLongPtr(w.hwnd, w32.GWL_STYLE)) + if currentStyle != 0 { + if b { + currentStyle |= style + } else { + currentStyle &^= style + } + w32.SetWindowLongPtr(w.hwnd, w32.GWL_STYLE, uintptr(currentStyle)) + } +} +func (w *windowsWebviewWindow) setExStyle(b bool, style int) { + currentStyle := int(w32.GetWindowLongPtr(w.hwnd, w32.GWL_EXSTYLE)) + if currentStyle != 0 { + if b { + currentStyle |= style + } else { + currentStyle &^= style + } + w32.SetWindowLongPtr(w.hwnd, w32.GWL_EXSTYLE, uintptr(currentStyle)) + } +} + +func (w *windowsWebviewWindow) setBackdropType(backdropType BackdropType) { + if !w32.IsWindowsVersionAtLeast(10, 0, 22621) { + var accent = w32.ACCENT_POLICY{ + AccentState: w32.ACCENT_ENABLE_BLURBEHIND, + } + var data w32.WINDOWCOMPOSITIONATTRIBDATA + data.Attrib = w32.WCA_ACCENT_POLICY + data.PvData = w32.PVOID(&accent) + data.CbData = w32.SIZE_T(unsafe.Sizeof(accent)) + + w32.SetWindowCompositionAttribute(w.hwnd, &data) + } else { + backdropValue := backdropType + // We default to None, but in w32 None = 1 and Auto = 0 + // So we check if the value given was Auto and set it to 0 + if backdropType == Auto { + backdropValue = None + } + w32.DwmSetWindowAttribute(w.hwnd, w32.DwmwaSystemBackdropType, w32.LPCVOID(&backdropValue), uint32(unsafe.Sizeof(backdropValue))) + } +} + +func (w *windowsWebviewWindow) setIcon(icon w32.HICON) { + w32.SendMessage(w.hwnd, w32.BM_SETIMAGE, w32.IMAGE_ICON, uintptr(icon)) +} + +func (w *windowsWebviewWindow) disableIcon() { + + // TODO: If frameless, return + exStyle := w32.GetWindowLong(w.hwnd, w32.GWL_EXSTYLE) + w32.SetWindowLong(w.hwnd, w32.GWL_EXSTYLE, uint32(exStyle|w32.WS_EX_DLGMODALFRAME)) + w32.SetWindowPos(w.hwnd, 0, 0, 0, 0, 0, + uint( + w32.SWP_FRAMECHANGED| + w32.SWP_NOMOVE| + w32.SWP_NOSIZE| + w32.SWP_NOZORDER), + ) +} + +func (w *windowsWebviewWindow) updateTheme(isDarkMode bool) { + + if w32.IsCurrentlyHighContrastMode() { + return + } + + if !w32.SupportsThemes() { + return + } + + w32.SetTheme(w.hwnd, isDarkMode) + + // Custom theme processing + customTheme := w.parent.options.Windows.CustomTheme + // Custom theme + if w32.SupportsCustomThemes() && customTheme != nil { + if w.isActive() { + if isDarkMode { + w32.SetTitleBarColour(w.hwnd, customTheme.DarkModeTitleBar) + w32.SetTitleTextColour(w.hwnd, customTheme.DarkModeTitleText) + w32.SetBorderColour(w.hwnd, customTheme.DarkModeBorder) + } else { + w32.SetTitleBarColour(w.hwnd, customTheme.LightModeTitleBar) + w32.SetTitleTextColour(w.hwnd, customTheme.LightModeTitleText) + w32.SetBorderColour(w.hwnd, customTheme.LightModeBorder) + } + } else { + if isDarkMode { + w32.SetTitleBarColour(w.hwnd, customTheme.DarkModeTitleBarInactive) + w32.SetTitleTextColour(w.hwnd, customTheme.DarkModeTitleTextInactive) + w32.SetBorderColour(w.hwnd, customTheme.DarkModeBorderInactive) + } else { + w32.SetTitleBarColour(w.hwnd, customTheme.LightModeTitleBarInactive) + w32.SetTitleTextColour(w.hwnd, customTheme.LightModeTitleTextInactive) + w32.SetBorderColour(w.hwnd, customTheme.LightModeBorderInactive) + } + } + } +} + +func (w *windowsWebviewWindow) isActive() bool { + return w32.GetForegroundWindow() == w.hwnd +} + +func (w *windowsWebviewWindow) WndProc(msg uint32, wparam, lparam uintptr) uintptr { + switch msg { + case w32.WM_SIZE: + return 0 + case w32.WM_CLOSE: + w32.PostMessage(w.hwnd, w32.WM_QUIT, 0, 0) + // Unregister the window with the application + windowsApp := globalApplication.impl.(*windowsApp) + windowsApp.unregisterWindow(w) + return 0 + case w32.WM_GETMINMAXINFO: + mmi := (*w32.MINMAXINFO)(unsafe.Pointer(lparam)) + hasConstraints := false + options := w.parent.options + if options.MinWidth > 0 || options.MinHeight > 0 { + hasConstraints = true + + width, height := w.scaleWithWindowDPI(options.MinWidth, options.MinHeight) + if width > 0 { + mmi.PtMinTrackSize.X = int32(width) + } + if height > 0 { + mmi.PtMinTrackSize.Y = int32(height) + } + } + if options.MaxWidth > 0 || options.MaxHeight > 0 { + hasConstraints = true + + width, height := w.scaleWithWindowDPI(options.MaxWidth, options.MaxHeight) + if width > 0 { + mmi.PtMaxTrackSize.X = int32(width) + } + if height > 0 { + mmi.PtMaxTrackSize.Y = int32(height) + } + } + if hasConstraints { + return 0 + } + case w32.WM_DPICHANGED: + newWindowSize := (*w32.RECT)(unsafe.Pointer(lparam)) + w32.SetWindowPos(w.hwnd, + uintptr(0), + int(newWindowSize.Left), + int(newWindowSize.Top), + int(newWindowSize.Right-newWindowSize.Left), + int(newWindowSize.Bottom-newWindowSize.Top), + w32.SWP_NOZORDER|w32.SWP_NOACTIVATE) + + } + + if options := w.parent.options; options.Frameless { + switch msg { + case w32.WM_ACTIVATE: + // If we want to have a frameless window but with the default frame decorations, extend the DWM client area. + // This Option is not affected by returning 0 in WM_NCCALCSIZE. + // As a result we have hidden the titlebar but still have the default window frame styling. + // See: https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmextendframeintoclientarea#remarks + if w.framelessWithDecorations() { + w32.ExtendFrameIntoClientArea(w.hwnd, true) + } + case w32.WM_NCHITTEST: + // Get the cursor position + x := int32(w32.LOWORD(uint32(lparam))) + y := int32(w32.HIWORD(uint32(lparam))) + ptCursor := w32.POINT{X: x, Y: y} + + // Get the window rectangle + rcWindow := w32.GetWindowRect(w.hwnd) + + // Determine if the cursor is in a resize area + bOnResizeBorder := false + resizeBorderWidth := int32(5) // change this to adjust the resize border width + if ptCursor.X >= rcWindow.Right-resizeBorderWidth { + bOnResizeBorder = true // right edge + } + if ptCursor.Y >= rcWindow.Bottom-resizeBorderWidth { + bOnResizeBorder = true // bottom edge + } + if ptCursor.X <= rcWindow.Left+resizeBorderWidth { + bOnResizeBorder = true // left edge + } + if ptCursor.Y <= rcWindow.Top+resizeBorderWidth { + bOnResizeBorder = true // top edge + } + + // Return the appropriate value + if bOnResizeBorder { + if ptCursor.X >= rcWindow.Right-resizeBorderWidth && ptCursor.Y >= rcWindow.Bottom-resizeBorderWidth { + return w32.HTBOTTOMRIGHT + } else if ptCursor.X <= rcWindow.Left+resizeBorderWidth && ptCursor.Y >= rcWindow.Bottom-resizeBorderWidth { + return w32.HTBOTTOMLEFT + } else if ptCursor.X >= rcWindow.Right-resizeBorderWidth && ptCursor.Y <= rcWindow.Top+resizeBorderWidth { + return w32.HTTOPRIGHT + } else if ptCursor.X <= rcWindow.Left+resizeBorderWidth && ptCursor.Y <= rcWindow.Top+resizeBorderWidth { + return w32.HTTOPLEFT + } else if ptCursor.X >= rcWindow.Right-resizeBorderWidth { + return w32.HTRIGHT + } else if ptCursor.Y >= rcWindow.Bottom-resizeBorderWidth { + return w32.HTBOTTOM + } else if ptCursor.X <= rcWindow.Left+resizeBorderWidth { + return w32.HTLEFT + } else if ptCursor.Y <= rcWindow.Top+resizeBorderWidth { + return w32.HTTOP + } + } + return w32.HTCLIENT + + case w32.WM_NCCALCSIZE: + // Disable the standard frame by allowing the client area to take the full + // window size. + // See: https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize#remarks + // This hides the titlebar and also disables the resizing from user interaction because the standard frame is not + // shown. We still need the WS_THICKFRAME style to enable resizing from the frontend. + if wparam != 0 { + rgrc := (*w32.RECT)(unsafe.Pointer(lparam)) + if w.isCurrentlyFullscreen { + // In Full-Screen mode we don't need to adjust anything + // It essential we have the flag here, that is set before SetWindowPos in fullscreen/unfullscreen + // because the native size might not yet reflect we are in fullscreen during this event! + // TODO: w.chromium.SetPadding(edge.Rect{}) + } else if w.isMaximised() { + // If the window is maximized we must adjust the client area to the work area of the monitor. Otherwise + // some content goes beyond the visible part of the monitor. + // Make sure to use the provided RECT to get the monitor, because during maximizig there might be + // a wrong monitor returned in multi screen mode when using MonitorFromWindow. + // See: https://github.com/MicrosoftEdge/WebView2Feedback/issues/2549 + monitor := w32.MonitorFromRect(rgrc, w32.MONITOR_DEFAULTTONULL) + + var monitorInfo w32.MONITORINFO + monitorInfo.CbSize = uint32(unsafe.Sizeof(monitorInfo)) + if monitor != 0 && w32.GetMonitorInfo(monitor, &monitorInfo) { + *rgrc = monitorInfo.RcWork + + maxWidth := options.MaxWidth + maxHeight := options.MaxHeight + if maxWidth > 0 || maxHeight > 0 { + var dpiX, dpiY uint + w32.GetDPIForMonitor(monitor, w32.MDT_EFFECTIVE_DPI, &dpiX, &dpiY) + + maxWidth := int32(ScaleWithDPI(maxWidth, dpiX)) + if maxWidth > 0 && rgrc.Right-rgrc.Left > maxWidth { + rgrc.Right = rgrc.Left + maxWidth + } + + maxHeight := int32(ScaleWithDPI(maxHeight, dpiY)) + if maxHeight > 0 && rgrc.Bottom-rgrc.Top > maxHeight { + rgrc.Bottom = rgrc.Top + maxHeight + } + } + } + // TODO: w.chromium.SetPadding(edge.Rect{}) + } else { + // This is needed to workaround the resize flickering in frameless mode with WindowDecorations + // See: https://stackoverflow.com/a/6558508 + // The workaround originally suggests to decrese the bottom 1px, but that seems to bring up a thin + // white line on some Windows-Versions, due to DrawBackground using also this reduces ClientSize. + // Increasing the bottom also worksaround the flickering but we would loose 1px of the WebView content + // therefore let's pad the content with 1px at the bottom. + rgrc.Bottom += 1 + // TODO: w.chromium.SetPadding(edge.Rect{Bottom: 1}) + } + return 0 + } + } + } + return w32.DefWindowProc(w.hwnd, msg, wparam, lparam) +} + +func (w *windowsWebviewWindow) DPI() (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(w.hwnd) + return dpi, dpi + } + + if w32.HasGetDPIForMonitorFunc() { + // GetDpiForWindow is supported beginning with Windows 8.1 + monitor := w32.MonitorFromWindow(w.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 (w *windowsWebviewWindow) scaleWithWindowDPI(width, height int) (int, int) { + dpix, dpiy := w.DPI() + scaledWidth := ScaleWithDPI(width, dpix) + scaledHeight := ScaleWithDPI(height, dpiy) + + return scaledWidth, scaledHeight +} + +func (w *windowsWebviewWindow) scaleToDefaultDPI(width, height int) (int, int) { + dpix, dpiy := w.DPI() + scaledWidth := ScaleToDefaultDPI(width, dpix) + scaledHeight := ScaleToDefaultDPI(height, dpiy) + + return scaledWidth, scaledHeight +} + +func ScaleWithDPI(pixels int, dpi uint) int { + return (pixels * int(dpi)) / 96 +} + +func ScaleToDefaultDPI(pixels int, dpi uint) int { + return (pixels * 96) / int(dpi) +} + +func NewIconFromResource(instance w32.HINSTANCE, resId uint16) (w32.HICON, error) { + var err error + var result w32.HICON + if result = w32.LoadIconWithResourceID(instance, resId); result == 0 { + err = errors.New(fmt.Sprintf("Cannot load icon from resource with id %v", resId)) + } + return result, err +} diff --git a/v3/pkg/events/events.go b/v3/pkg/events/events.go index f26f53b2e..5e97f358c 100644 --- a/v3/pkg/events/events.go +++ b/v3/pkg/events/events.go @@ -260,3 +260,25 @@ func newMacEvents() macEvents { WindowFileDraggingExited: 1145, } } + +var Windows = newWindowsEvents() + +type windowsEvents struct { + SystemThemeChanged ApplicationEventType + APMPowerStatusChange ApplicationEventType + APMSuspend ApplicationEventType + APMResumeAutomatic ApplicationEventType + APMResumeSuspend ApplicationEventType + APMPowerSettingChange ApplicationEventType +} + +func newWindowsEvents() windowsEvents { + return windowsEvents{ + SystemThemeChanged: 1146, + APMPowerStatusChange: 1147, + APMSuspend: 1148, + APMResumeAutomatic: 1149, + APMResumeSuspend: 1150, + APMPowerSettingChange: 1151, + } +} diff --git a/v3/pkg/events/events.txt b/v3/pkg/events/events.txt index dbbe98db4..e1ddfb262 100644 --- a/v3/pkg/events/events.txt +++ b/v3/pkg/events/events.txt @@ -120,4 +120,9 @@ mac:WebViewDidCommitNavigation mac:WindowFileDraggingEntered mac:WindowFileDraggingPerformed mac:WindowFileDraggingExited - +windows:SystemThemeChanged +windows:APMPowerStatusChange +windows:APMSuspend +windows:APMResumeAutomatic +windows:APMResumeSuspend +windows:APMPowerSettingChange diff --git a/v3/pkg/w32/clipboard.go b/v3/pkg/w32/clipboard.go new file mode 100644 index 000000000..89334c0a4 --- /dev/null +++ b/v3/pkg/w32/clipboard.go @@ -0,0 +1,143 @@ +//go:build windows + +/* + * Based on code originally from https://github.com/atotto/clipboard. Copyright (c) 2013 Ato Araki. All rights reserved. + */ + +package w32 + +import ( + "runtime" + "syscall" + "time" + "unsafe" +) + +const ( + cfUnicodetext = 13 + gmemMoveable = 0x0002 +) + +// waitOpenClipboard opens the clipboard, waiting for up to a second to do so. +func waitOpenClipboard() error { + started := time.Now() + limit := started.Add(time.Second) + var r uintptr + var err error + for time.Now().Before(limit) { + r, _, err = procOpenClipboard.Call(0) + if r != 0 { + return nil + } + time.Sleep(time.Millisecond) + } + return err +} + +func GetClipboardText() (string, error) { + // LockOSThread ensure that the whole method will keep executing on the same thread from begin to end (it actually locks the goroutine thread attribution). + // Otherwise if the goroutine switch thread during execution (which is a common practice), the OpenClipboard and CloseClipboard will happen on two different threads, and it will result in a clipboard deadlock. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + if formatAvailable, _, err := procIsClipboardFormatAvailable.Call(cfUnicodetext); formatAvailable == 0 { + return "", err + } + err := waitOpenClipboard() + if err != nil { + return "", err + } + + h, _, err := procGetClipboardData.Call(cfUnicodetext) + if h == 0 { + _, _, _ = procCloseClipboard.Call() + return "", err + } + + l, _, err := kernelGlobalLock.Call(h) + if l == 0 { + _, _, _ = procCloseClipboard.Call() + return "", err + } + + text := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(l))[:]) + + r, _, err := kernelGlobalUnlock.Call(h) + if r == 0 { + _, _, _ = procCloseClipboard.Call() + return "", err + } + + closed, _, err := procCloseClipboard.Call() + if closed == 0 { + return "", err + } + return text, nil +} + +func SetClipboardText(text string) error { + // LockOSThread ensure that the whole method will keep executing on the same thread from begin to end (it actually locks the goroutine thread attribution). + // Otherwise if the goroutine switch thread during execution (which is a common practice), the OpenClipboard and CloseClipboard will happen on two different threads, and it will result in a clipboard deadlock. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + err := waitOpenClipboard() + if err != nil { + return err + } + + r, _, err := procEmptyClipboard.Call(0) + if r == 0 { + _, _, _ = procCloseClipboard.Call() + return err + } + + data, err := syscall.UTF16FromString(text) + if err != nil { + return err + } + + // "If the hMem parameter identifies a memory object, the object must have + // been allocated using the function with the GMEM_MOVEABLE flag." + h, _, err := kernelGlobalAlloc.Call(gmemMoveable, uintptr(len(data)*int(unsafe.Sizeof(data[0])))) + if h == 0 { + _, _, _ = procCloseClipboard.Call() + return err + } + defer func() { + if h != 0 { + kernelGlobalFree.Call(h) + } + }() + + l, _, err := kernelGlobalLock.Call(h) + if l == 0 { + _, _, _ = procCloseClipboard.Call() + return err + } + + r, _, err = kernelLstrcpy.Call(l, uintptr(unsafe.Pointer(&data[0]))) + if r == 0 { + _, _, _ = procCloseClipboard.Call() + return err + } + + r, _, err = kernelGlobalUnlock.Call(h) + if r == 0 { + if err.(syscall.Errno) != 0 { + _, _, _ = procCloseClipboard.Call() + return err + } + } + + r, _, err = procSetClipboardData.Call(cfUnicodetext, h) + if r == 0 { + _, _, _ = procCloseClipboard.Call() + return err + } + h = 0 // suppress deferred cleanup + closed, _, err := procCloseClipboard.Call() + if closed == 0 { + return err + } + return nil +} diff --git a/v3/pkg/w32/comctl32.go b/v3/pkg/w32/comctl32.go new file mode 100644 index 000000000..b66709f5f --- /dev/null +++ b/v3/pkg/w32/comctl32.go @@ -0,0 +1,112 @@ +//go:build windows + +/* + * 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/v3/pkg/w32/comdlg32.go b/v3/pkg/w32/comdlg32.go new file mode 100644 index 000000000..d28922c33 --- /dev/null +++ b/v3/pkg/w32/comdlg32.go @@ -0,0 +1,40 @@ +//go:build windows + +/* + * 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/v3/pkg/w32/constants.go b/v3/pkg/w32/constants.go new file mode 100644 index 000000000..a06db4fda --- /dev/null +++ b/v3/pkg/w32/constants.go @@ -0,0 +1,3551 @@ +//go:build windows + +/* + * 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 +) + +const ( + GW_HWNDFIRST = 0 + GW_HWNDLAST = 1 + GW_HWNDNEXT = 2 + GW_HWNDPREV = 3 + GW_OWNER = 4 + GW_CHILD = 5 + GW_ENABLEDPOPUP = 6 +) + +// 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_DPICHANGED = 0x02E0 +) + +// 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 +) + +const ( + // PBT_APMPOWERSTATUSCHANGE - Power status has changed. + PBT_APMPOWERSTATUSCHANGE = 10 + + // PBT_APMRESUMEAUTOMATIC -Operation is resuming automatically from a low-power state. This message is sent every time the system resumes. + PBT_APMRESUMEAUTOMATIC = 18 + + // PBT_APMRESUMESUSPEND - Operation is resuming from a low-power state. This message is sent after PBT_APMRESUMEAUTOMATIC if the resume is triggered by user input, such as pressing a key. + PBT_APMRESUMESUSPEND = 7 + + // PBT_APMSUSPEND - System is suspending operation. + PBT_APMSUSPEND = 4 + + // PBT_POWERSETTINGCHANGE - A power setting change event has been received. + PBT_POWERSETTINGCHANGE = 32787 +) + +// 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 +) + +// 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/v3/pkg/w32/consts.go b/v3/pkg/w32/consts.go new file mode 100644 index 000000000..b9afe2794 --- /dev/null +++ b/v3/pkg/w32/consts.go @@ -0,0 +1,83 @@ +//go:build windows + +package w32 + +import ( + "golang.org/x/sys/windows/registry" + "strconv" + "syscall" +) + +var ( + modwingdi = syscall.NewLazyDLL("gdi32.dll") + procCreateSolidBrush = modwingdi.NewProc("CreateSolidBrush") +) +var ( + kernel32 = syscall.NewLazyDLL("kernel32") + kernelGlobalAlloc = kernel32.NewProc("GlobalAlloc") + kernelGlobalFree = kernel32.NewProc("GlobalFree") + kernelGlobalLock = kernel32.NewProc("GlobalLock") + kernelGlobalUnlock = kernel32.NewProc("GlobalUnlock") + kernelLstrcpy = kernel32.NewProc("lstrcpyW") +) + +var windowsVersion, _ = getWindowsVersionInfo() + +func IsWindowsVersionAtLeast(major, minor, buildNumber int) bool { + return windowsVersion.Major >= major && + windowsVersion.Minor >= minor && + windowsVersion.Build >= buildNumber +} + +type WindowsVersionInfo struct { + Major int + Minor int + Build int + DisplayVersion string +} + +func (w *WindowsVersionInfo) IsWindowsVersionAtLeast(major, minor, buildNumber int) bool { + return w.Major >= major && w.Minor >= minor && w.Build >= buildNumber +} + +func getWindowsVersionInfo() (*WindowsVersionInfo, error) { + key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) + if err != nil { + return nil, err + } + + return &WindowsVersionInfo{ + Major: regDWORDKeyAsInt(key, "CurrentMajorVersionNumber"), + Minor: regDWORDKeyAsInt(key, "CurrentMinorVersionNumber"), + Build: regStringKeyAsInt(key, "CurrentBuildNumber"), + DisplayVersion: regKeyAsString(key, "DisplayVersion"), + }, nil +} + +func regDWORDKeyAsInt(key registry.Key, name string) int { + result, _, err := key.GetIntegerValue(name) + if err != nil { + return -1 + } + return int(result) +} + +func regStringKeyAsInt(key registry.Key, name string) int { + resultStr, _, err := key.GetStringValue(name) + if err != nil { + return -1 + } + result, err := strconv.Atoi(resultStr) + if err != nil { + return -1 + } + return result +} + +func regKeyAsString(key registry.Key, name string) string { + resultStr, _, err := key.GetStringValue(name) + if err != nil { + return "" + } + return resultStr +} diff --git a/v3/pkg/w32/dwmapi.go b/v3/pkg/w32/dwmapi.go new file mode 100644 index 000000000..4d3effeb4 --- /dev/null +++ b/v3/pkg/w32/dwmapi.go @@ -0,0 +1,36 @@ +//go:build windows + +package w32 + +import ( + "syscall" + "unsafe" +) + +var ( + moddwmapi = syscall.NewLazyDLL("dwmapi.dll") + + procDwmSetWindowAttribute = moddwmapi.NewProc("DwmSetWindowAttribute") + procDwmExtendFrameIntoClientArea = moddwmapi.NewProc("DwmExtendFrameIntoClientArea") +) + +func DwmSetWindowAttribute(hwnd HWND, dwAttribute DWMWINDOWATTRIBUTE, pvAttribute LPCVOID, cbAttribute uint32) HRESULT { + ret, _, _ := procDwmSetWindowAttribute.Call( + hwnd, + uintptr(dwAttribute), + uintptr(pvAttribute), + uintptr(cbAttribute)) + return HRESULT(ret) +} + +func dwmExtendFrameIntoClientArea(hwnd uintptr, margins *MARGINS) error { + ret, _, _ := procDwmExtendFrameIntoClientArea.Call( + hwnd, + uintptr(unsafe.Pointer(margins))) + + if ret != 0 { + return syscall.GetLastError() + } + + return nil +} diff --git a/v3/pkg/w32/gdi32.go b/v3/pkg/w32/gdi32.go new file mode 100644 index 000000000..b4b9053e6 --- /dev/null +++ b/v3/pkg/w32/gdi32.go @@ -0,0 +1,526 @@ +//go:build windows + +/* + * 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/v3/pkg/w32/gdiplus.go b/v3/pkg/w32/gdiplus.go new file mode 100644 index 000000000..2591ed71b --- /dev/null +++ b/v3/pkg/w32/gdiplus.go @@ -0,0 +1,177 @@ +//go:build windows + +/* + * 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/v3/pkg/w32/idispatch.go b/v3/pkg/w32/idispatch.go new file mode 100644 index 000000000..4f610f3ff --- /dev/null +++ b/v3/pkg/w32/idispatch.go @@ -0,0 +1,45 @@ +//go:build windows + +/* + * 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/v3/pkg/w32/istream.go b/v3/pkg/w32/istream.go new file mode 100644 index 000000000..a47fbbce1 --- /dev/null +++ b/v3/pkg/w32/istream.go @@ -0,0 +1,33 @@ +//go:build windows + +/* + * 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/v3/pkg/w32/iunknown.go b/v3/pkg/w32/iunknown.go new file mode 100644 index 000000000..8ddc605cc --- /dev/null +++ b/v3/pkg/w32/iunknown.go @@ -0,0 +1,29 @@ +//go:build windows + +/* + * 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/v3/pkg/w32/kernel32.go b/v3/pkg/w32/kernel32.go new file mode 100644 index 000000000..063a1b0ea --- /dev/null +++ b/v3/pkg/w32/kernel32.go @@ -0,0 +1,332 @@ +//go:build windows + +/* + * 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/v3/pkg/w32/ole32.go b/v3/pkg/w32/ole32.go new file mode 100644 index 000000000..004099316 --- /dev/null +++ b/v3/pkg/w32/ole32.go @@ -0,0 +1,65 @@ +//go:build windows + +/* + * 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/v3/pkg/w32/oleaut32.go b/v3/pkg/w32/oleaut32.go new file mode 100644 index 000000000..0bb8ef7da --- /dev/null +++ b/v3/pkg/w32/oleaut32.go @@ -0,0 +1,50 @@ +//go:build windows + +/* + * 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/v3/pkg/w32/screen.go b/v3/pkg/w32/screen.go new file mode 100644 index 000000000..7f43beb2b --- /dev/null +++ b/v3/pkg/w32/screen.go @@ -0,0 +1,118 @@ +//go:build windows + +package w32 + +import ( + "fmt" + "syscall" + "unsafe" +) + +func MonitorsEqual(first MONITORINFO, second MONITORINFO) bool { + // Checks to make sure all the fields are the same. + // A cleaner way would be to check identity of devices. but I couldn't find a way of doing that using the win32 API + return first.DwFlags == second.DwFlags && + first.RcMonitor.Top == second.RcMonitor.Top && + first.RcMonitor.Bottom == second.RcMonitor.Bottom && + first.RcMonitor.Right == second.RcMonitor.Right && + first.RcMonitor.Left == second.RcMonitor.Left && + first.RcWork.Top == second.RcWork.Top && + first.RcWork.Bottom == second.RcWork.Bottom && + first.RcWork.Right == second.RcWork.Right && + first.RcWork.Left == second.RcWork.Left +} + +func GetMonitorInformation(hMonitor HMONITOR) (*MONITORINFO, error) { + // Adapted from winc.utils.getMonitorInfo + // See docs for + //https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmonitorinfoa + + var info MONITORINFO + info.CbSize = uint32(unsafe.Sizeof(info)) + succeeded := GetMonitorInfo(hMonitor, &info) + if !succeeded { + return &info, fmt.Errorf("Windows call to getMonitorInfo failed") + } + return &info, nil +} + +type Screen struct { + IsCurrent bool + IsPrimary bool + Width int + Height int +} + +func EnumProc(hMonitor HMONITOR, hdcMonitor HDC, lprcMonitor *RECT, screenContainer *ScreenContainer) uintptr { + // adapted from https://stackoverflow.com/a/23492886/4188138 + + // see docs for the following pages to better understand this function + // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors + // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-monitorenumproc + // https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-monitorinfo + // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfromwindow + + ourMonitorData := Screen{} + currentMonHndl := MonitorFromWindow(screenContainer.mainWinHandle, MONITOR_DEFAULTTONEAREST) + currentMonInfo, currErr := GetMonitorInformation(currentMonHndl) + + if currErr != nil { + screenContainer.errors = append(screenContainer.errors, currErr) + screenContainer.monitors = append(screenContainer.monitors, Screen{}) + // not sure what the consequences of returning false are, so let's just return true and handle it ourselves + return TRUE + } + + monInfo, err := GetMonitorInformation(hMonitor) + if err != nil { + screenContainer.errors = append(screenContainer.errors, err) + screenContainer.monitors = append(screenContainer.monitors, Screen{}) + return TRUE + } + + height := lprcMonitor.Right - lprcMonitor.Left + width := lprcMonitor.Bottom - lprcMonitor.Top + ourMonitorData.IsPrimary = monInfo.DwFlags&MONITORINFOF_PRIMARY == 1 + ourMonitorData.Height = int(width) + ourMonitorData.Width = int(height) + ourMonitorData.IsCurrent = MonitorsEqual(*currentMonInfo, *monInfo) + + // the reason we need a container is that we have don't know how many times this function will be called + // this "append" call could potentially do an allocation and rewrite the pointer to monitors. So we save the pointer in screenContainer.monitors + // and retrieve the values after all EnumProc calls + // If EnumProc is multi-threaded, this could be problematic. Although, I don't think it is. + screenContainer.monitors = append(screenContainer.monitors, ourMonitorData) + // let's keep screenContainer.errors the same size as screenContainer.monitors in case we want to match them up later if necessary + screenContainer.errors = append(screenContainer.errors, nil) + return TRUE +} + +type ScreenContainer struct { + monitors []Screen + errors []error + mainWinHandle HWND +} + +func GetAllScreens(mainWinHandle HWND) ([]Screen, error) { + // TODO fix hack of container sharing by having a proper data sharing mechanism between windows and the runtime + monitorContainer := ScreenContainer{mainWinHandle: mainWinHandle} + returnErr := error(nil) + var errorStrings []string + + dc := GetDC(0) + defer ReleaseDC(0, dc) + succeeded := EnumDisplayMonitors(dc, nil, syscall.NewCallback(EnumProc), unsafe.Pointer(&monitorContainer)) + if !succeeded { + return monitorContainer.monitors, fmt.Errorf("Windows call to EnumDisplayMonitors failed") + } + for idx, err := range monitorContainer.errors { + if err != nil { + errorStrings = append(errorStrings, fmt.Sprintf("Error from monitor #%v, %v", idx+1, err)) + } + } + + if len(errorStrings) > 0 { + returnErr = fmt.Errorf("%v errors encountered: %v", len(errorStrings), errorStrings) + } + return monitorContainer.monitors, returnErr +} diff --git a/v3/pkg/w32/shcore.go b/v3/pkg/w32/shcore.go new file mode 100644 index 000000000..72e9aab3d --- /dev/null +++ b/v3/pkg/w32/shcore.go @@ -0,0 +1,29 @@ +//go:build windows + +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/v3/pkg/w32/shell32.go b/v3/pkg/w32/shell32.go new file mode 100644 index 000000000..458fbc645 --- /dev/null +++ b/v3/pkg/w32/shell32.go @@ -0,0 +1,235 @@ +//go:build windows + +/* + * 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/v3/pkg/w32/shlwapi.go b/v3/pkg/w32/shlwapi.go new file mode 100644 index 000000000..89d17ce6f --- /dev/null +++ b/v3/pkg/w32/shlwapi.go @@ -0,0 +1,26 @@ +//go:build windows + +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/v3/pkg/w32/theme.go b/v3/pkg/w32/theme.go new file mode 100644 index 000000000..1b358789f --- /dev/null +++ b/v3/pkg/w32/theme.go @@ -0,0 +1,119 @@ +//go:build windows + +package w32 + +import ( + "unsafe" + + "golang.org/x/sys/windows/registry" +) + +type DWMWINDOWATTRIBUTE int32 + +const DwmwaUseImmersiveDarkModeBefore20h1 DWMWINDOWATTRIBUTE = 19 +const DwmwaUseImmersiveDarkMode DWMWINDOWATTRIBUTE = 20 +const DwmwaBorderColor DWMWINDOWATTRIBUTE = 34 +const DwmwaCaptionColor DWMWINDOWATTRIBUTE = 35 +const DwmwaTextColor DWMWINDOWATTRIBUTE = 36 +const DwmwaSystemBackdropType DWMWINDOWATTRIBUTE = 38 + +const SPI_GETHIGHCONTRAST = 0x0042 +const HCF_HIGHCONTRASTON = 0x00000001 + +// BackdropType defines the type of translucency we wish to use +type BackdropType int32 + +func dwmSetWindowAttribute(hwnd uintptr, dwAttribute DWMWINDOWATTRIBUTE, pvAttribute unsafe.Pointer, cbAttribute uintptr) { + ret, _, err := procDwmSetWindowAttribute.Call( + hwnd, + uintptr(dwAttribute), + uintptr(pvAttribute), + cbAttribute) + if ret != 0 { + _ = err + // println(err.Error()) + } +} + +func SupportsThemes() bool { + // We can't support Windows versions before 17763 + return IsWindowsVersionAtLeast(10, 0, 17763) +} + +func SupportsCustomThemes() bool { + return IsWindowsVersionAtLeast(10, 0, 17763) +} + +func SupportsBackdropTypes() bool { + return IsWindowsVersionAtLeast(10, 0, 22621) +} + +func SupportsImmersiveDarkMode() bool { + return IsWindowsVersionAtLeast(10, 0, 18985) +} + +func SetTheme(hwnd uintptr, useDarkMode bool) { + if SupportsThemes() { + attr := DwmwaUseImmersiveDarkModeBefore20h1 + if SupportsImmersiveDarkMode() { + attr = DwmwaUseImmersiveDarkMode + } + var winDark int32 + if useDarkMode { + winDark = 1 + } + dwmSetWindowAttribute(hwnd, attr, unsafe.Pointer(&winDark), unsafe.Sizeof(winDark)) + } +} + +func EnableTranslucency(hwnd uintptr, backdrop BackdropType) { + if SupportsBackdropTypes() { + dwmSetWindowAttribute(hwnd, DwmwaSystemBackdropType, unsafe.Pointer(&backdrop), unsafe.Sizeof(backdrop)) + } else { + println("Warning: Translucency type unavailable on Windows < 22621") + } +} + +func SetTitleBarColour(hwnd uintptr, titleBarColour int32) { + dwmSetWindowAttribute(hwnd, DwmwaCaptionColor, unsafe.Pointer(&titleBarColour), unsafe.Sizeof(titleBarColour)) +} + +func SetTitleTextColour(hwnd uintptr, titleTextColour int32) { + dwmSetWindowAttribute(hwnd, DwmwaTextColor, unsafe.Pointer(&titleTextColour), unsafe.Sizeof(titleTextColour)) +} + +func SetBorderColour(hwnd uintptr, titleBorderColour int32) { + dwmSetWindowAttribute(hwnd, DwmwaBorderColor, unsafe.Pointer(&titleBorderColour), unsafe.Sizeof(titleBorderColour)) +} + +func IsCurrentlyDarkMode() bool { + key, err := registry.OpenKey(registry.CURRENT_USER, `SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize`, registry.QUERY_VALUE) + if err != nil { + return false + } + defer key.Close() + + AppsUseLightTheme, _, err := key.GetIntegerValue("AppsUseLightTheme") + if err != nil { + return false + } + return AppsUseLightTheme == 0 +} + +type highContrast struct { + CbSize uint32 + DwFlags uint32 + LpszDefaultScheme *int16 +} + +func IsCurrentlyHighContrastMode() bool { + var result highContrast + result.CbSize = uint32(unsafe.Sizeof(result)) + res, _, err := procSystemParametersInfo.Call(SPI_GETHIGHCONTRAST, uintptr(result.CbSize), uintptr(unsafe.Pointer(&result)), 0) + if res == 0 { + _ = err + return false + } + r := result.DwFlags&HCF_HIGHCONTRASTON == HCF_HIGHCONTRASTON + return r +} diff --git a/v3/pkg/w32/toolbar.go b/v3/pkg/w32/toolbar.go new file mode 100644 index 000000000..ac9261fc4 --- /dev/null +++ b/v3/pkg/w32/toolbar.go @@ -0,0 +1,216 @@ +//go:build windows + +/* + * 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/v3/pkg/w32/typedef.go b/v3/pkg/w32/typedef.go new file mode 100644 index 000000000..13735204c --- /dev/null +++ b/v3/pkg/w32/typedef.go @@ -0,0 +1,1081 @@ +//go:build windows + +/* + * 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/v3/pkg/w32/user32.go b/v3/pkg/w32/user32.go new file mode 100644 index 000000000..002836c1f --- /dev/null +++ b/v3/pkg/w32/user32.go @@ -0,0 +1,1313 @@ +//go:build windows + +/* + * 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") + procGetWindow = moduser32.NewProc("GetWindow") + 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") + procSetProcessDPIAware = moduser32.NewProc("SetProcessDPIAware") + 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") + procGetForegroundWindow = moduser32.NewProc("GetForegroundWindow") + + procSystemParametersInfo = moduser32.NewProc("SystemParametersInfoW") + procSetClassLong = moduser32.NewProc("SetClassLongW") + procSetClassLongPtr = moduser32.NewProc("SetClassLongPtrW") + + 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 LoadIconWithResourceID(instance HINSTANCE, res uint16) HICON { + ret, _, _ := procLoadIcon.Call( + uintptr(instance), + uintptr(res)) + + return HICON(ret) +} + +func LoadCursor(instance HINSTANCE, cursorName *uint16) HCURSOR { + ret, _, _ := procLoadCursor.Call( + uintptr(instance), + uintptr(unsafe.Pointer(cursorName))) + + return HCURSOR(ret) +} + +func LoadCursorWithResourceID(instance HINSTANCE, res uint16) HCURSOR { + ret, _, _ := procLoadCursor.Call( + uintptr(instance), + uintptr(res)) + + 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 SetProcessDPIAware() error { + status, r, err := procSetProcessDPIAware.Call() + if status == 0 { + return fmt.Errorf("SetProcessDPIAware failed %d: %v %v", status, r, err) + } + return nil +} + +func GetForegroundWindow() HWND { + ret, _, _ := procGetForegroundWindow.Call() + return HWND(ret) +} + +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 GetWindow(hwnd HWND, cmd uint32) HWND { + ret, _, _ := procGetWindow.Call( + hwnd, + uintptr(cmd), + ) + return HWND(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 GetMonitorInfoEx(hMonitor HMONITOR, lmpi *MONITORINFOEX) bool { + ret, _, _ := procGetMonitorInfo.Call( + uintptr(hMonitor), + uintptr(unsafe.Pointer(lmpi)), + ) + return ret != 0 +} + +func EnumDisplayMonitors(hdc HDC, clip *RECT, fnEnum uintptr, dwData unsafe.Pointer) bool { + ret, _, _ := procEnumDisplayMonitors.Call( + hdc, + uintptr(unsafe.Pointer(clip)), + fnEnum, + uintptr(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/v3/pkg/w32/utils.go b/v3/pkg/w32/utils.go new file mode 100644 index 000000000..8a72d4846 --- /dev/null +++ b/v3/pkg/w32/utils.go @@ -0,0 +1,230 @@ +//go:build windows + +/* + * 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 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/v3/pkg/w32/uxtheme.go b/v3/pkg/w32/uxtheme.go new file mode 100644 index 000000000..51ec0035f --- /dev/null +++ b/v3/pkg/w32/uxtheme.go @@ -0,0 +1,152 @@ +//go:build windows + +/* + * 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/v3/pkg/w32/vars.go b/v3/pkg/w32/vars.go new file mode 100644 index 000000000..cb69f9d19 --- /dev/null +++ b/v3/pkg/w32/vars.go @@ -0,0 +1,16 @@ +//go:build windows + +/* + * 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/v3/pkg/w32/window.go b/v3/pkg/w32/window.go new file mode 100644 index 000000000..fec225cfe --- /dev/null +++ b/v3/pkg/w32/window.go @@ -0,0 +1,164 @@ +//go:build windows +package w32 + +import ( + "fmt" + "github.com/samber/lo" + "log" + "strconv" + "syscall" + "unsafe" +) + +const ( + GCLP_HBRBACKGROUND int32 = -10 +) + +func ExtendFrameIntoClientArea(hwnd uintptr, extend bool) { + // -1: Adds the default frame styling (aero shadow and e.g. rounded corners on Windows 11) + // Also shows the caption buttons if transparent ant translucent but they don't work. + // 0: Adds the default frame styling but no aero shadow, does not show the caption buttons. + // 1: Adds the default frame styling (aero shadow and e.g. rounded corners on Windows 11) but no caption buttons + // are shown if transparent ant translucent. + var margins MARGINS + if extend { + margins = MARGINS{1, 1, 1, 1} // Only extend 1 pixel to have the default frame styling but no caption buttons + } + if err := dwmExtendFrameIntoClientArea(hwnd, &margins); err != nil { + log.Fatal(fmt.Errorf("DwmExtendFrameIntoClientArea failed: %s", err)) + } +} + +func IsVisible(hwnd uintptr) bool { + ret, _, _ := procIsWindowVisible.Call(hwnd) + return ret != 0 +} + +func IsWindowFullScreen(hwnd uintptr) bool { + wRect := GetWindowRect(hwnd) + m := MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY) + var mi MONITORINFO + mi.CbSize = uint32(unsafe.Sizeof(mi)) + if !GetMonitorInfo(m, &mi) { + return false + } + return wRect.Left == mi.RcMonitor.Left && + wRect.Top == mi.RcMonitor.Top && + wRect.Right == mi.RcMonitor.Right && + wRect.Bottom == mi.RcMonitor.Bottom +} + +func IsWindowMaximised(hwnd uintptr) bool { + style := uint32(getWindowLong(hwnd, GWL_STYLE)) + return style&WS_MAXIMIZE != 0 +} +func IsWindowMinimised(hwnd uintptr) bool { + style := uint32(getWindowLong(hwnd, GWL_STYLE)) + return style&WS_MINIMIZE != 0 +} + +func RestoreWindow(hwnd uintptr) { + showWindow(hwnd, SW_RESTORE) +} + +func ShowWindowMaximised(hwnd uintptr) { + showWindow(hwnd, SW_MAXIMIZE) +} +func ShowWindowMinimised(hwnd uintptr) { + showWindow(hwnd, SW_MINIMIZE) +} + +func SetBackgroundColour(hwnd uintptr, r, g, b uint8) { + col := uint32(r) | uint32(g)<<8 | uint32(b)<<16 + hbrush, _, _ := procCreateSolidBrush.Call(uintptr(col)) + setClassLongPtr(hwnd, GCLP_HBRBACKGROUND, hbrush) +} + +func IsWindowNormal(hwnd uintptr) bool { + return !IsWindowMaximised(hwnd) && !IsWindowMinimised(hwnd) && !IsWindowFullScreen(hwnd) +} + +func setClassLongPtr(hwnd uintptr, param int32, val uintptr) bool { + proc := procSetClassLongPtr + if strconv.IntSize == 32 { + /* + https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setclasslongptrw + Note: To write code that is compatible with both 32-bit and 64-bit Windows, use SetClassLongPtr. + When compiling for 32-bit Windows, SetClassLongPtr is defined as a call to the SetClassLong function + + => We have to do this dynamically when directly calling the DLL procedures + */ + proc = procSetClassLong + } + + ret, _, _ := proc.Call( + hwnd, + uintptr(param), + val, + ) + return ret != 0 +} + +func getWindowLong(hwnd uintptr, index int) int32 { + ret, _, _ := procGetWindowLong.Call( + hwnd, + uintptr(index)) + + return int32(ret) +} + +func showWindow(hwnd uintptr, cmdshow int) bool { + ret, _, _ := procShowWindow.Call( + hwnd, + uintptr(cmdshow)) + return ret != 0 +} + +func MustStringToUTF16Ptr(input string) *uint16 { + return lo.Must(syscall.UTF16PtrFromString(input)) +} + +func MustStringToUTF16uintptr(input string) uintptr { + ret := lo.Must(syscall.UTF16PtrFromString(input)) + return uintptr(unsafe.Pointer(ret)) +} + +func MustStringToUTF16(input string) []uint16 { + return lo.Must(syscall.UTF16FromString(input)) +} + +func CenterWindow(hwnd HWND) { + windowInfo := getWindowInfo(hwnd) + frameless := windowInfo.IsPopup() + + info := GetMonitorInfoForWindow(hwnd) + workRect := info.RcWork + screenMiddleW := workRect.Left + (workRect.Right-workRect.Left)/2 + screenMiddleH := workRect.Top + (workRect.Bottom-workRect.Top)/2 + var winRect *RECT + if !frameless { + winRect = GetWindowRect(hwnd) + } else { + winRect = GetClientRect(hwnd) + } + winWidth := winRect.Right - winRect.Left + winHeight := winRect.Bottom - winRect.Top + windowX := screenMiddleW - (winWidth / 2) + windowY := screenMiddleH - (winHeight / 2) + SetWindowPos(hwnd, HWND_TOP, int(windowX), int(windowY), int(winWidth), int(winHeight), SWP_NOSIZE) +} + +func getWindowInfo(hwnd HWND) *WINDOWINFO { + var info WINDOWINFO + info.CbSize = uint32(unsafe.Sizeof(info)) + GetWindowInfo(hwnd, &info) + return &info +} + +func GetMonitorInfoForWindow(hwnd HWND) *MONITORINFO { + currentMonitor := MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) + var info MONITORINFO + info.CbSize = uint32(unsafe.Sizeof(info)) + GetMonitorInfo(currentMonitor, &info) + return &info +} diff --git a/v3/tasks/events/generate.go b/v3/tasks/events/generate.go index eafa27f89..2c5869a1e 100644 --- a/v3/tasks/events/generate.go +++ b/v3/tasks/events/generate.go @@ -25,6 +25,18 @@ func newMacEvents() macEvents { return macEvents{ $$MACEVENTSVALUES } } + +var Windows = newWindowsEvents() + +type windowsEvents struct { +$$WINDOWSEVENTSDECL} + +func newWindowsEvents() windowsEvents { + return windowsEvents{ +$$WINDOWSEVENTSVALUES } +} + + ` var eventsH = `//go:build darwin @@ -53,7 +65,11 @@ func main() { applicationDelegateEvents := bytes.NewBufferString("") webviewDelegateEvents := bytes.NewBufferString("") + windowsEventsDecl := bytes.NewBufferString("") + windowsEventsValues := bytes.NewBufferString("") + var id int + var maxMacEvents int var line []byte // Loop over each line in the file for id, line = range bytes.Split(eventNames, []byte{'\n'}) { @@ -94,6 +110,7 @@ func main() { macEventsDecl.WriteString("\t" + eventTitle + " " + eventType + "\n") macEventsValues.WriteString("\t\t" + event + ": " + strconv.Itoa(id) + ",\n") cHeaderEvents.WriteString("#define Event" + eventTitle + " " + strconv.Itoa(id) + "\n") + maxMacEvents = id if ignoreEvent { continue } @@ -128,15 +145,61 @@ func main() { `) } - + case "windows": + eventType := "ApplicationEventType" + if strings.HasPrefix(event, "Window") { + eventType = "WindowEventType" + } + if strings.HasPrefix(event, "WebView") { + eventType = "WindowEventType" + } + windowsEventsDecl.WriteString("\t" + eventTitle + " " + eventType + "\n") + windowsEventsValues.WriteString("\t\t" + event + ": " + strconv.Itoa(id) + ",\n") + // cHeaderEvents.WriteString("#define Event" + eventTitle + " " + strconv.Itoa(id) + "\n") + // if ignoreEvent { + // continue + // } + // // Check if this is a window event + // if strings.HasPrefix(event, "Window") { + // windowDelegateEvents.WriteString(`- (void)` + delegateEventFunction + `:(NSNotification *)notification { + // if( hasListeners(Event` + eventTitle + `) ) { + // processWindowEvent(self.windowId, Event` + eventTitle + `); + // } + //} + // + //`) + // } + // // Check if this is a webview event + // if strings.HasPrefix(event, "WebView") { + // webViewFunction := strings.TrimPrefix(event, "WebView") + // webViewFunction = string(bytes.ToLower([]byte{webViewFunction[0]})) + webViewFunction[1:] + // webviewDelegateEvents.WriteString(`- (void)webView:(WKWebView *)webview ` + webViewFunction + `:(WKNavigation *)navigation { + // if( hasListeners(Event` + eventTitle + `) ) { + // processWindowEvent(self.windowId, Event` + eventTitle + `); + // } + //} + // + //`) + // } + // if strings.HasPrefix(event, "Application") { + // applicationDelegateEvents.WriteString(`- (void)` + delegateEventFunction + `:(NSNotification *)notification { + // if( hasListeners(Event` + eventTitle + `) ) { + // processApplicationEvent(Event` + eventTitle + `); + // } + //} + // + //`) + // } } } - cHeaderEvents.WriteString("\n#define MAX_EVENTS " + strconv.Itoa(id-1) + "\n") + cHeaderEvents.WriteString("\n#define MAX_EVENTS " + strconv.Itoa(maxMacEvents+1) + "\n") // Save the eventsGo template substituting the values and decls templateToWrite := strings.ReplaceAll(eventsGo, "$$MACEVENTSDECL", macEventsDecl.String()) templateToWrite = strings.ReplaceAll(templateToWrite, "$$MACEVENTSVALUES", macEventsValues.String()) + templateToWrite = strings.ReplaceAll(templateToWrite, "$$WINDOWSEVENTSDECL", windowsEventsDecl.String()) + templateToWrite = strings.ReplaceAll(templateToWrite, "$$WINDOWSEVENTSVALUES", windowsEventsValues.String()) err = os.WriteFile("../../pkg/events/events.go", []byte(templateToWrite), 0644) if err != nil { panic(err) diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index 0a0ab8359..eb69875ff 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -14,10 +14,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Breaking Changes + +- `wails dev` now uses the custom schemes `wails://` on macOS and Linux if Vite >= `v3.0.0` is used. This makes the dev application consistent in behaviour with the final production application and fixes some long-standing inconsistencies. Changed by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2610) + ### Added - Added Nodejs version in `wails doctor`. Added by @misitebao in [PR](https://github.com/wailsapp/wails/pull/2546) - Added support for WebKit2GTK 2.40+ on Linux. This brings additional features for the [AssetServer](/docs/reference/options#assetserver), like support for HTTP Request Bodies. The app must be compiled with the Go build tag `webkit2_40` to activate support for this features. This also bumps the minimum requirement of WebKit2GTK to 2.40 for your app. Added by @stffabi in this [PR](https://github.com/wailsapp/wails/pull/2592) +- macOS: Added Window menu role with well known shortcuts "Minimize, Full-Screen and Zoom". Added by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2586) +- macOS: Added "Hide, Hide Others, Show All“ to appmenu. Added by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2586) ### Changed