window_goxjs.go 13 KB

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