window_goxjs.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. //go:build js || wasm || test_web_driver
  2. // +build js wasm test_web_driver
  3. package glfw
  4. import (
  5. "context"
  6. _ "image/png" // for the icon
  7. "runtime"
  8. "sync"
  9. "fyne.io/fyne/v2"
  10. "fyne.io/fyne/v2/driver/desktop"
  11. "fyne.io/fyne/v2/internal"
  12. "fyne.io/fyne/v2/internal/driver/common"
  13. "fyne.io/fyne/v2/internal/painter/gl"
  14. "github.com/fyne-io/glfw-js"
  15. )
  16. type Cursor struct {
  17. }
  18. const defaultTitle = "Fyne Application"
  19. // Input modes.
  20. const (
  21. CursorMode glfw.InputMode = glfw.CursorMode
  22. StickyKeysMode glfw.InputMode = glfw.StickyKeysMode
  23. StickyMouseButtonsMode glfw.InputMode = glfw.StickyMouseButtonsMode
  24. LockKeyMods glfw.InputMode = glfw.LockKeyMods
  25. RawMouseMotion glfw.InputMode = glfw.RawMouseMotion
  26. )
  27. // Cursor mode values.
  28. const (
  29. CursorNormal int = glfw.CursorNormal
  30. CursorHidden int = glfw.CursorHidden
  31. CursorDisabled int = glfw.CursorDisabled
  32. )
  33. var cursorMap map[desktop.Cursor]*Cursor
  34. // Declare conformity to Window interface
  35. var _ fyne.Window = (*window)(nil)
  36. type window struct {
  37. common.Window
  38. viewport *glfw.Window
  39. viewLock sync.RWMutex
  40. createLock sync.Once
  41. decorate bool
  42. closing bool
  43. fixedSize bool
  44. cursor desktop.Cursor
  45. canvas *glCanvas
  46. driver *gLDriver
  47. title string
  48. icon fyne.Resource
  49. mainmenu *fyne.MainMenu
  50. clipboard fyne.Clipboard
  51. master bool
  52. fullScreen bool
  53. centered bool
  54. visible bool
  55. mouseLock sync.RWMutex
  56. mousePos fyne.Position
  57. mouseDragged fyne.Draggable
  58. mouseDraggedObjStart fyne.Position
  59. mouseDraggedOffset fyne.Position
  60. mouseDragPos fyne.Position
  61. mouseDragStarted bool
  62. mouseButton desktop.MouseButton
  63. mouseOver desktop.Hoverable
  64. mouseLastClick fyne.CanvasObject
  65. mousePressed fyne.CanvasObject
  66. mouseClickCount int
  67. mouseCancelFunc context.CancelFunc
  68. onClosed func()
  69. onCloseIntercepted func()
  70. menuTogglePending fyne.KeyName
  71. menuDeactivationPending fyne.KeyName
  72. xpos, ypos int
  73. width, height int
  74. requestedWidth, requestedHeight int
  75. shouldWidth, shouldHeight int
  76. shouldExpand bool
  77. pending []func()
  78. }
  79. func (w *window) SetFullScreen(full bool) {
  80. w.fullScreen = true
  81. }
  82. // centerOnScreen handles the logic for centering a window
  83. func (w *window) CenterOnScreen() {
  84. // FIXME: not supported with WebGL
  85. w.centered = true
  86. }
  87. func (w *window) doCenterOnScreen() {
  88. // FIXME: no meaning for defining center on screen in WebGL
  89. }
  90. func (w *window) RequestFocus() {
  91. // FIXME: no meaning for defining focus in WebGL
  92. }
  93. func (w *window) SetIcon(icon fyne.Resource) {
  94. // FIXME: no support for SetIcon yet
  95. }
  96. func (w *window) SetMaster() {
  97. // FIXME: there could really only be one window
  98. }
  99. func (w *window) fitContent() {
  100. w.shouldWidth, w.shouldHeight = w.requestedWidth, w.requestedHeight
  101. }
  102. func (w *window) getMonitorForWindow() *glfw.Monitor {
  103. return glfw.GetPrimaryMonitor()
  104. }
  105. func scaleForDpi(xdpi int) float32 {
  106. switch {
  107. case xdpi > 1000:
  108. // assume that this is a mistake and bail
  109. return float32(1.0)
  110. case xdpi > 192:
  111. return float32(1.5)
  112. case xdpi > 144:
  113. return float32(1.35)
  114. case xdpi > 120:
  115. return float32(1.2)
  116. default:
  117. return float32(1.0)
  118. }
  119. }
  120. func (w *window) detectScale() float32 {
  121. return scaleForDpi(int(96))
  122. }
  123. func (w *window) moved(_ *glfw.Window, x, y int) {
  124. w.processMoved(x, y)
  125. }
  126. func (w *window) resized(_ *glfw.Window, width, height int) {
  127. w.canvas.scale = w.calculatedScale()
  128. w.processResized(width, height)
  129. }
  130. func (w *window) frameSized(_ *glfw.Window, width, height int) {
  131. w.processFrameSized(width, height)
  132. }
  133. func (w *window) refresh(_ *glfw.Window) {
  134. w.processRefresh()
  135. }
  136. func (w *window) closed(viewport *glfw.Window) {
  137. viewport.SetShouldClose(false) // reset the closed flag until we check the veto in processClosed
  138. w.processClosed()
  139. }
  140. func fyneToNativeCursor(cursor desktop.Cursor) (*Cursor, bool) {
  141. return nil, false
  142. }
  143. func (w *window) SetCursor(_ *Cursor) {
  144. }
  145. func (w *window) setCustomCursor(rawCursor *Cursor, isCustomCursor bool) {
  146. }
  147. func (w *window) mouseMoved(_ *glfw.Window, xpos, ypos float64) {
  148. w.processMouseMoved(w.scaleInput(xpos), w.scaleInput(ypos))
  149. }
  150. func (w *window) mouseClicked(viewport *glfw.Window, btn glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
  151. button, modifiers := convertMouseButton(btn, mods)
  152. mouseAction := convertAction(action)
  153. w.processMouseClicked(button, mouseAction, modifiers)
  154. }
  155. func (w *window) mouseScrolled(viewport *glfw.Window, xoff, yoff float64) {
  156. if runtime.GOOS != "darwin" && xoff == 0 &&
  157. (viewport.GetKey(glfw.KeyLeftShift) == glfw.Press ||
  158. viewport.GetKey(glfw.KeyRightShift) == glfw.Press) {
  159. xoff, yoff = yoff, xoff
  160. }
  161. w.processMouseScrolled(xoff, yoff)
  162. }
  163. func convertMouseButton(btn glfw.MouseButton, mods glfw.ModifierKey) (desktop.MouseButton, fyne.KeyModifier) {
  164. modifier := desktopModifier(mods)
  165. var button desktop.MouseButton
  166. rightClick := false
  167. if runtime.GOOS == "darwin" {
  168. if modifier&fyne.KeyModifierControl != 0 {
  169. rightClick = true
  170. modifier &^= fyne.KeyModifierControl
  171. }
  172. if modifier&fyne.KeyModifierSuper != 0 {
  173. modifier |= fyne.KeyModifierControl
  174. modifier &^= fyne.KeyModifierSuper
  175. }
  176. }
  177. switch btn {
  178. case glfw.MouseButton1:
  179. if rightClick {
  180. button = desktop.RightMouseButton
  181. } else {
  182. button = desktop.LeftMouseButton
  183. }
  184. case glfw.MouseButton2:
  185. button = desktop.RightMouseButton
  186. }
  187. return button, modifier
  188. }
  189. //gocyclo:ignore
  190. func glfwKeyToKeyName(key glfw.Key) fyne.KeyName {
  191. switch key {
  192. // numbers - lookup by code to avoid AZERTY using the symbol name instead of number
  193. case glfw.Key0, glfw.KeyKP0:
  194. return fyne.Key0
  195. case glfw.Key1, glfw.KeyKP1:
  196. return fyne.Key1
  197. case glfw.Key2, glfw.KeyKP2:
  198. return fyne.Key2
  199. case glfw.Key3, glfw.KeyKP3:
  200. return fyne.Key3
  201. case glfw.Key4, glfw.KeyKP4:
  202. return fyne.Key4
  203. case glfw.Key5, glfw.KeyKP5:
  204. return fyne.Key5
  205. case glfw.Key6, glfw.KeyKP6:
  206. return fyne.Key6
  207. case glfw.Key7, glfw.KeyKP7:
  208. return fyne.Key7
  209. case glfw.Key8, glfw.KeyKP8:
  210. return fyne.Key8
  211. case glfw.Key9, glfw.KeyKP9:
  212. return fyne.Key9
  213. // non-printable
  214. case glfw.KeyEscape:
  215. return fyne.KeyEscape
  216. case glfw.KeyEnter:
  217. return fyne.KeyReturn
  218. case glfw.KeyTab:
  219. return fyne.KeyTab
  220. case glfw.KeyBackspace:
  221. return fyne.KeyBackspace
  222. case glfw.KeyInsert:
  223. return fyne.KeyInsert
  224. case glfw.KeyDelete:
  225. return fyne.KeyDelete
  226. case glfw.KeyRight:
  227. return fyne.KeyRight
  228. case glfw.KeyLeft:
  229. return fyne.KeyLeft
  230. case glfw.KeyDown:
  231. return fyne.KeyDown
  232. case glfw.KeyUp:
  233. return fyne.KeyUp
  234. case glfw.KeyPageUp:
  235. return fyne.KeyPageUp
  236. case glfw.KeyPageDown:
  237. return fyne.KeyPageDown
  238. case glfw.KeyHome:
  239. return fyne.KeyHome
  240. case glfw.KeyEnd:
  241. return fyne.KeyEnd
  242. case glfw.KeySpace:
  243. return fyne.KeySpace
  244. case glfw.KeyKPEnter:
  245. return fyne.KeyEnter
  246. // desktop
  247. case glfw.KeyLeftShift:
  248. return desktop.KeyShiftLeft
  249. case glfw.KeyRightShift:
  250. return desktop.KeyShiftRight
  251. case glfw.KeyLeftControl:
  252. return desktop.KeyControlLeft
  253. case glfw.KeyRightControl:
  254. return desktop.KeyControlRight
  255. case glfw.KeyLeftAlt:
  256. return desktop.KeyAltLeft
  257. case glfw.KeyRightAlt:
  258. return desktop.KeyAltRight
  259. case glfw.KeyLeftSuper:
  260. return desktop.KeySuperLeft
  261. case glfw.KeyRightSuper:
  262. return desktop.KeySuperRight
  263. case glfw.KeyMenu:
  264. return desktop.KeyMenu
  265. case glfw.KeyPrintScreen:
  266. return desktop.KeyPrintScreen
  267. case glfw.KeyCapsLock:
  268. return desktop.KeyCapsLock
  269. // functions
  270. case glfw.KeyF1:
  271. return fyne.KeyF1
  272. case glfw.KeyF2:
  273. return fyne.KeyF2
  274. case glfw.KeyF3:
  275. return fyne.KeyF3
  276. case glfw.KeyF4:
  277. return fyne.KeyF4
  278. case glfw.KeyF5:
  279. return fyne.KeyF5
  280. case glfw.KeyF6:
  281. return fyne.KeyF6
  282. case glfw.KeyF7:
  283. return fyne.KeyF7
  284. case glfw.KeyF8:
  285. return fyne.KeyF8
  286. case glfw.KeyF9:
  287. return fyne.KeyF9
  288. case glfw.KeyF10:
  289. return fyne.KeyF10
  290. case glfw.KeyF11:
  291. return fyne.KeyF11
  292. case glfw.KeyF12:
  293. return fyne.KeyF12
  294. }
  295. return fyne.KeyUnknown
  296. }
  297. func keyCodeToKeyName(code string) fyne.KeyName {
  298. if len(code) != 1 {
  299. return fyne.KeyUnknown
  300. }
  301. char := code[0]
  302. if char >= 'a' && char <= 'z' {
  303. // Our alphabetical keys are all upper case characters.
  304. return fyne.KeyName('A' + char - 'a')
  305. }
  306. switch char {
  307. case '[':
  308. return fyne.KeyLeftBracket
  309. case '\\':
  310. return fyne.KeyBackslash
  311. case ']':
  312. return fyne.KeyRightBracket
  313. case '\'':
  314. return fyne.KeyApostrophe
  315. case ',':
  316. return fyne.KeyComma
  317. case '-':
  318. return fyne.KeyMinus
  319. case '.':
  320. return fyne.KeyPeriod
  321. case '/':
  322. return fyne.KeySlash
  323. case '*':
  324. return fyne.KeyAsterisk
  325. case '`':
  326. return fyne.KeyBackTick
  327. case ';':
  328. return fyne.KeySemicolon
  329. case '+':
  330. return fyne.KeyPlus
  331. case '=':
  332. return fyne.KeyEqual
  333. }
  334. return fyne.KeyUnknown
  335. }
  336. func keyToName(code glfw.Key, scancode int) fyne.KeyName {
  337. if runtime.GOOS == "darwin" && scancode == 0x69 { // TODO remove once fixed upstream glfw/glfw#1786
  338. code = glfw.KeyPrintScreen
  339. }
  340. ret := glfwKeyToKeyName(code)
  341. if ret != fyne.KeyUnknown {
  342. return ret
  343. }
  344. // keyName := glfw.GetKeyName(code, scancode)
  345. // return keyCodeToKeyName(keyName)
  346. return fyne.KeyUnknown
  347. }
  348. func convertAction(action glfw.Action) action {
  349. switch action {
  350. case glfw.Press:
  351. return press
  352. case glfw.Release:
  353. return release
  354. case glfw.Repeat:
  355. return repeat
  356. }
  357. panic("Could not convert glfw.Action.")
  358. }
  359. func convertASCII(key glfw.Key) fyne.KeyName {
  360. if key < glfw.KeyA || key > glfw.KeyZ {
  361. return fyne.KeyUnknown
  362. }
  363. return fyne.KeyName(rune(key))
  364. }
  365. func (w *window) keyPressed(viewport *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
  366. keyName := keyToName(key, scancode)
  367. keyDesktopModifier := desktopModifier(mods)
  368. keyAction := convertAction(action)
  369. keyASCII := convertASCII(key)
  370. w.processKeyPressed(keyName, keyASCII, scancode, keyAction, keyDesktopModifier)
  371. }
  372. func desktopModifier(mods glfw.ModifierKey) fyne.KeyModifier {
  373. var m fyne.KeyModifier
  374. if (mods & glfw.ModShift) != 0 {
  375. m |= fyne.KeyModifierShift
  376. }
  377. if (mods & glfw.ModControl) != 0 {
  378. m |= fyne.KeyModifierControl
  379. }
  380. if (mods & glfw.ModAlt) != 0 {
  381. m |= fyne.KeyModifierAlt
  382. }
  383. if (mods & glfw.ModSuper) != 0 {
  384. m |= fyne.KeyModifierSuper
  385. }
  386. return m
  387. }
  388. // charInput defines the character with modifiers callback which is called when a
  389. // Unicode character is input regardless of what modifier keys are used.
  390. //
  391. // Characters do not map 1:1 to physical keys, as a key may produce zero, one or more characters.
  392. func (w *window) charInput(viewport *glfw.Window, char rune) {
  393. w.processCharInput(char)
  394. }
  395. func (w *window) focused(_ *glfw.Window, focused bool) {
  396. w.processFocused(focused)
  397. }
  398. func (w *window) DetachCurrentContext() {
  399. glfw.DetachCurrentContext()
  400. }
  401. func (w *window) rescaleOnMain() {
  402. if w.viewport == nil {
  403. return
  404. }
  405. // if w.fullScreen {
  406. w.width, w.height = w.viewport.GetSize()
  407. scaledFull := fyne.NewSize(
  408. internal.UnscaleInt(w.canvas, w.width),
  409. internal.UnscaleInt(w.canvas, w.height))
  410. w.canvas.Resize(scaledFull)
  411. return
  412. // }
  413. // size := w.canvas.size.Union(w.canvas.MinSize())
  414. // newWidth, newHeight := w.screenSize(size)
  415. // w.viewport.SetSize(newWidth, newHeight)
  416. }
  417. func (w *window) create() {
  418. runOnMain(func() {
  419. // we can't hide the window in webgl, so there might be some artifact
  420. initWindowHints()
  421. pixWidth, pixHeight := w.screenSize(w.canvas.size)
  422. pixWidth = int(fyne.Max(float32(pixWidth), float32(w.width)))
  423. if pixWidth == 0 {
  424. pixWidth = 10
  425. }
  426. pixHeight = int(fyne.Max(float32(pixHeight), float32(w.height)))
  427. if pixHeight == 0 {
  428. pixHeight = 10
  429. }
  430. win, err := glfw.CreateWindow(pixWidth, pixHeight, w.title, nil, nil)
  431. if err != nil {
  432. w.driver.initFailed("window creation error", err)
  433. return
  434. }
  435. w.viewLock.Lock()
  436. w.viewport = win
  437. w.viewLock.Unlock()
  438. })
  439. if w.view() == nil { // something went wrong above, it will have been logged
  440. return
  441. }
  442. // run the GL init on the draw thread
  443. runOnDraw(w, func() {
  444. w.canvas.SetPainter(gl.NewPainter(w.canvas, w))
  445. w.canvas.Painter().Init()
  446. })
  447. runOnMain(func() {
  448. w.setDarkMode()
  449. win := w.view()
  450. win.SetCloseCallback(w.closed)
  451. win.SetPosCallback(w.moved)
  452. win.SetSizeCallback(w.resized)
  453. win.SetFramebufferSizeCallback(w.frameSized)
  454. win.SetRefreshCallback(w.refresh)
  455. win.SetCursorPosCallback(w.mouseMoved)
  456. win.SetMouseButtonCallback(w.mouseClicked)
  457. win.SetScrollCallback(w.mouseScrolled)
  458. win.SetKeyCallback(w.keyPressed)
  459. win.SetCharCallback(w.charInput)
  460. win.SetFocusCallback(w.focused)
  461. w.canvas.detectedScale = w.detectScale()
  462. w.canvas.scale = w.calculatedScale()
  463. w.canvas.texScale = w.detectTextureScale()
  464. // update window size now we have scaled detected
  465. w.fitContent()
  466. for _, fn := range w.pending {
  467. fn()
  468. }
  469. w.requestedWidth, w.requestedHeight = w.width, w.height
  470. width, height := win.GetSize()
  471. w.processFrameSized(width, height)
  472. w.processResized(width, height)
  473. })
  474. }
  475. func (w *window) view() *glfw.Window {
  476. w.viewLock.RLock()
  477. defer w.viewLock.RUnlock()
  478. if w.closing {
  479. return nil
  480. }
  481. return w.viewport
  482. }