Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add function to obtain Window handle. #4483

Closed
2 tasks done
MiyamuraMiyako opened this issue Dec 24, 2023 · 9 comments
Closed
2 tasks done

Add function to obtain Window handle. #4483

MiyamuraMiyako opened this issue Dec 24, 2023 · 9 comments

Comments

@MiyamuraMiyako
Copy link

MiyamuraMiyako commented Dec 24, 2023

Checklist

  • I have searched the issue tracker for open issues that relate to the same feature, before opening a new one.
  • This issue only relates to a single feature. I will open new issues for any other features.

Is your feature request related to a problem?

I need use handle pointer to set Window's other attribute.

Is it possible to construct a solution with the existing API?

No response

Describe the solution you'd like to see.

w := d.CreateSplashWindow()
w.Show()
var hwnd := w.Handle()
winapi.SetWindowPos(hwnd,..............)
@andydotxyz
Copy link
Member

How do you imagine this working universally for all platforms?
The "Other attributes" may not work either, can you be more specific?

For the most part API that are not exposed are done so for good reason.

@MatejMagat305
Copy link
Contributor

maybe with funkcion RunOnNative, it could be posible ...

@Jacalz
Copy link
Member

Jacalz commented Jan 1, 2024

There is so much that can go wrong here. Linux and BSD have different window handles for X11 and Wayland, macOS has one, Windows has one, Android has one and so on.

@Jacalz
Copy link
Member

Jacalz commented Jan 1, 2024

Partially related to #1155

@dweymouth
Copy link
Contributor

dweymouth commented Feb 7, 2024

I can see why this is desired and sometimes necessary for certain things outside of Fyne's scope, like there are certain WinAPIs that must be called with an HWND (like integrating with System Media Transport Controls for media player apps (API)). I wonder if there could be a case for a Fyne unsafe package or something where we expose APIs that most apps shouldn't need, and don't have guaranteed similar behavior on all platforms?

@andydotxyz
Copy link
Member

I'm not sure how you would do that as the information is internal to each driver internal package - and per OS too.
I wonder if we need to treat this like the RunNative. That has ben discussed in passing before, providing a way to run code on the correct thread whilst passing in required context (you are not allowed to do antything with the window handles off-main on macOS and others).

@aynakeya
Copy link

aynakeya commented Apr 9, 2024

For those need a temporary way to obtain a window handle in desktop environment (linux,windows.macos).

Here is a working solution.

var w fyne.Window

func main() {
	a := app.NewWithID("io.fyne.demo")
	w = a.NewWindow("Fyne Demo")
	go func() {
                time.Sleep(2 * time.Second) 
		println("Window handle:", xfyne.GetWindowHandle(w))
	}()
	w.Resize(fyne.NewSize(1080, 720))
	w.ShowAndRun()
}

also noted that window handle is assigned only after the window has been displayed.

xfyne utils is defined by following file

window.go

package xfyne

import (
	"fyne.io/fyne/v2"
	"github.com/go-gl/glfw/v3.3/glfw"
	"reflect"
	"unsafe"
)

// getGlfwWindow returns the glfw.Window pointer from a fyne.Window.
// very unsafe and ugly hacks. but it works.
func getGlfwWindow(window fyne.Window) *glfw.Window {
	rv := reflect.ValueOf(window)
	if rv.Type().String() != "*glfw.window" {
		return nil
	}
	rv = rv.Elem()
	var glfwWindowPtr uintptr = rv.FieldByName("viewport").Pointer()
        // uncomment following code to wait until window is displayed
	// for glfwWindowPtr == 0 {
	// 	 glfwWindowPtr = rv.FieldByName("viewport").Pointer()
	// }
	return (*glfw.Window)(unsafe.Pointer(glfwWindowPtr))
}

window_darwin.go

//go:build darwin
// +build darwin

package xfyne

func GetWindowHandle(window fyne.Window) uintptr {
	glfwWindow := getGlfwWindow(window)
	if glfwWindow == nil {
		return 0
	}
	return uintptr(glfwWindow.GetCocoaWindow())
}

window_linux.go

//go:build linux
// +build linux

package xfyne

import (
	"fyne.io/fyne/v2"
)

func GetWindowHandle(window fyne.Window) uintptr {
	glfwWindow := getGlfwWindow(window)
	if glfwWindow == nil {
		return 0
	}
	return uintptr(glfwWindow.GetX11Window())
}

window_windows.go

//go:build windows
// +build windows

package xfyne

import "fyne.io/fyne/v2"

func GetWindowHandle(window fyne.Window) uintptr {
	glfwWindow := getGlfwWindow(window)
	if glfwWindow == nil {
		return 0
	}
	return uintptr(glfwWindow.GetWin32Window())
}

@dweymouth
Copy link
Contributor

On develop for 2.5.0

@dweymouth
Copy link
Contributor

Here is a quick example of how to use the new API:

package main

import (
	"log"
	"runtime"

	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/driver"
	"fyne.io/fyne/v2/widget"
)

func main() {
	app := app.New()
	win := app.NewWindow("hello")
	win.SetContent(widget.NewLabel("Hello world!"))

	app.Lifecycle().SetOnEnteredForeground(func() {
		// we will not have a window pointer until it is shown,
		// so use lifecycle to wait until then
		nativeWin, ok := win.(driver.NativeWindow)
		if !ok {
			panic("this will never happen for a top-level window")
		}
		nativeWin.RunNative(func(ctx any) {
			switch runtime.GOOS {
			case "windows":
				hwnd := ctx.(driver.WindowsWindowContext).HWND
				log.Printf("HWND is %x\n", hwnd)
				//callSomeWinAPICGoWrapper(hwnd)
			case "darwin":
				nsWindow := ctx.(driver.MacWindowContext).NSWindow
				log.Printf("NSWindow ptr is %x", nsWindow)
				//callSomeCocoaAPICGoWrapper(nsWindow)
			case "linux":
				if wayland, ok := ctx.(driver.WaylandWindowContext); ok {
					wlSurface := wayland.WaylandSurface
					log.Printf("Wayland window ptr is %x", wlSurface)
					// do something with Wayland surface pointer
				} else {
					x11Window := ctx.(driver.X11WindowContext).WindowHandle
					log.Printf("X11 window ptr is %x", x11Window)
					// do something with X11 window pointer
				}
			}
		})
	})

	win.ShowAndRun()
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants