window_desktop.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. //go:build !js && !wasm && !test_web_driver
  2. // +build !js,!wasm,!test_web_driver
  3. package glfw
  4. import (
  5. "bytes"
  6. "context"
  7. "image"
  8. _ "image/png" // for the icon
  9. "runtime"
  10. "sync"
  11. "fyne.io/fyne/v2"
  12. "fyne.io/fyne/v2/canvas"
  13. "fyne.io/fyne/v2/driver/desktop"
  14. "fyne.io/fyne/v2/internal"
  15. "fyne.io/fyne/v2/internal/driver/common"
  16. "fyne.io/fyne/v2/internal/painter"
  17. "fyne.io/fyne/v2/internal/painter/gl"
  18. "github.com/go-gl/glfw/v3.3/glfw"
  19. )
  20. const defaultTitle = "Fyne Application"
  21. // Input modes.
  22. const (
  23. CursorMode glfw.InputMode = glfw.CursorMode
  24. StickyKeysMode glfw.InputMode = glfw.StickyKeysMode
  25. StickyMouseButtonsMode glfw.InputMode = glfw.StickyMouseButtonsMode
  26. LockKeyMods glfw.InputMode = glfw.LockKeyMods
  27. RawMouseMotion glfw.InputMode = glfw.RawMouseMotion
  28. )
  29. // Cursor mode values.
  30. const (
  31. CursorNormal int = glfw.CursorNormal
  32. CursorHidden int = glfw.CursorHidden
  33. CursorDisabled int = glfw.CursorDisabled
  34. )
  35. var cursorMap map[desktop.StandardCursor]*glfw.Cursor
  36. func initCursors() {
  37. cursorMap = map[desktop.StandardCursor]*glfw.Cursor{
  38. desktop.DefaultCursor: glfw.CreateStandardCursor(glfw.ArrowCursor),
  39. desktop.TextCursor: glfw.CreateStandardCursor(glfw.IBeamCursor),
  40. desktop.CrosshairCursor: glfw.CreateStandardCursor(glfw.CrosshairCursor),
  41. desktop.PointerCursor: glfw.CreateStandardCursor(glfw.HandCursor),
  42. desktop.HResizeCursor: glfw.CreateStandardCursor(glfw.HResizeCursor),
  43. desktop.VResizeCursor: glfw.CreateStandardCursor(glfw.VResizeCursor),
  44. desktop.HiddenCursor: nil,
  45. }
  46. }
  47. // Declare conformity to Window interface
  48. var _ fyne.Window = (*window)(nil)
  49. type window struct {
  50. common.Window
  51. viewport *glfw.Window
  52. viewLock sync.RWMutex
  53. createLock sync.Once
  54. decorate bool
  55. closing bool
  56. fixedSize bool
  57. cursor desktop.Cursor
  58. customCursor *glfw.Cursor
  59. canvas *glCanvas
  60. driver *gLDriver
  61. title string
  62. icon fyne.Resource
  63. mainmenu *fyne.MainMenu
  64. clipboard fyne.Clipboard
  65. master bool
  66. fullScreen bool
  67. centered bool
  68. visible bool
  69. mouseLock sync.RWMutex
  70. mousePos fyne.Position
  71. mouseDragged fyne.Draggable
  72. mouseDraggedObjStart fyne.Position
  73. mouseDraggedOffset fyne.Position
  74. mouseDragPos fyne.Position
  75. mouseDragStarted bool
  76. mouseButton desktop.MouseButton
  77. mouseOver desktop.Hoverable
  78. mouseLastClick fyne.CanvasObject
  79. mousePressed fyne.CanvasObject
  80. mouseClickCount int
  81. mouseCancelFunc context.CancelFunc
  82. onClosed func()
  83. onCloseIntercepted func()
  84. menuTogglePending fyne.KeyName
  85. menuDeactivationPending fyne.KeyName
  86. xpos, ypos int
  87. width, height int
  88. requestedWidth, requestedHeight int
  89. shouldWidth, shouldHeight int
  90. shouldExpand bool
  91. pending []func()
  92. }
  93. func (w *window) SetFullScreen(full bool) {
  94. w.fullScreen = full
  95. if !w.visible {
  96. return
  97. }
  98. runOnMain(func() {
  99. monitor := w.getMonitorForWindow()
  100. mode := monitor.GetVideoMode()
  101. if full {
  102. w.viewport.SetMonitor(monitor, 0, 0, mode.Width, mode.Height, mode.RefreshRate)
  103. } else {
  104. if w.width == 0 && w.height == 0 { // if we were fullscreen on creation...
  105. w.width, w.height = w.screenSize(w.canvas.Size())
  106. }
  107. w.viewport.SetMonitor(nil, w.xpos, w.ypos, w.width, w.height, 0)
  108. }
  109. })
  110. }
  111. func (w *window) CenterOnScreen() {
  112. w.centered = true
  113. if w.view() != nil {
  114. runOnMain(w.doCenterOnScreen)
  115. }
  116. }
  117. func (w *window) doCenterOnScreen() {
  118. viewWidth, viewHeight := w.screenSize(w.canvas.size)
  119. if w.width > viewWidth { // in case our window has not called back to canvas size yet
  120. viewWidth = w.width
  121. }
  122. if w.height > viewHeight {
  123. viewHeight = w.height
  124. }
  125. // get window dimensions in pixels
  126. monitor := w.getMonitorForWindow()
  127. monMode := monitor.GetVideoMode()
  128. // these come into play when dealing with multiple monitors
  129. monX, monY := monitor.GetPos()
  130. // math them to the middle
  131. newX := (monMode.Width-viewWidth)/2 + monX
  132. newY := (monMode.Height-viewHeight)/2 + monY
  133. // set new window coordinates
  134. w.viewport.SetPos(newX, newY)
  135. }
  136. func (w *window) RequestFocus() {
  137. if isWayland || w.view() == nil {
  138. return
  139. }
  140. w.runOnMainWhenCreated(w.viewport.Focus)
  141. }
  142. func (w *window) SetIcon(icon fyne.Resource) {
  143. w.icon = icon
  144. if icon == nil {
  145. appIcon := fyne.CurrentApp().Icon()
  146. if appIcon != nil {
  147. w.SetIcon(appIcon)
  148. }
  149. return
  150. }
  151. w.runOnMainWhenCreated(func() {
  152. if w.icon == nil {
  153. w.viewport.SetIcon(nil)
  154. return
  155. }
  156. var img image.Image
  157. if painter.IsResourceSVG(w.icon) {
  158. img = painter.PaintImage(&canvas.Image{Resource: w.icon}, nil, windowIconSize, windowIconSize)
  159. } else {
  160. pix, _, err := image.Decode(bytes.NewReader(w.icon.Content()))
  161. if err != nil {
  162. fyne.LogError("Failed to decode image for window icon", err)
  163. return
  164. }
  165. img = pix
  166. }
  167. w.viewport.SetIcon([]image.Image{img})
  168. })
  169. }
  170. func (w *window) SetMaster() {
  171. w.master = true
  172. }
  173. func (w *window) fitContent() {
  174. if w.canvas.Content() == nil || (w.fullScreen && w.visible) {
  175. return
  176. }
  177. if w.isClosing() {
  178. return
  179. }
  180. minWidth, minHeight := w.minSizeOnScreen()
  181. w.viewLock.RLock()
  182. view := w.viewport
  183. w.viewLock.RUnlock()
  184. w.shouldWidth, w.shouldHeight = w.width, w.height
  185. if w.width < minWidth || w.height < minHeight {
  186. if w.width < minWidth {
  187. w.shouldWidth = minWidth
  188. }
  189. if w.height < minHeight {
  190. w.shouldHeight = minHeight
  191. }
  192. w.viewLock.Lock()
  193. w.shouldExpand = true // queue the resize to happen on main
  194. w.viewLock.Unlock()
  195. }
  196. if w.fixedSize {
  197. if w.shouldWidth > w.requestedWidth {
  198. w.requestedWidth = w.shouldWidth
  199. }
  200. if w.shouldHeight > w.requestedHeight {
  201. w.requestedHeight = w.shouldHeight
  202. }
  203. view.SetSizeLimits(w.requestedWidth, w.requestedHeight, w.requestedWidth, w.requestedHeight)
  204. } else {
  205. view.SetSizeLimits(minWidth, minHeight, glfw.DontCare, glfw.DontCare)
  206. }
  207. }
  208. func (w *window) getMonitorForWindow() *glfw.Monitor {
  209. x, y := w.xpos, w.ypos
  210. if w.fullScreen {
  211. x, y = w.viewport.GetPos()
  212. }
  213. xOff := x + (w.width / 2)
  214. yOff := y + (w.height / 2)
  215. for _, monitor := range glfw.GetMonitors() {
  216. x, y := monitor.GetPos()
  217. if x > xOff || y > yOff {
  218. continue
  219. }
  220. if x+monitor.GetVideoMode().Width <= xOff || y+monitor.GetVideoMode().Height <= yOff {
  221. continue
  222. }
  223. return monitor
  224. }
  225. // try built-in function to detect monitor if above logic didn't succeed
  226. // if it doesn't work then return primary monitor as default
  227. monitor := w.viewport.GetMonitor()
  228. if monitor == nil {
  229. monitor = glfw.GetPrimaryMonitor()
  230. }
  231. return monitor
  232. }
  233. func (w *window) detectScale() float32 {
  234. if isWayland { // Wayland controls scale through content scaling
  235. return 1.0
  236. }
  237. monitor := w.getMonitorForWindow()
  238. if monitor == nil {
  239. return 1.0
  240. }
  241. widthMm, _ := monitor.GetPhysicalSize()
  242. widthPx := monitor.GetVideoMode().Width
  243. return calculateDetectedScale(widthMm, widthPx)
  244. }
  245. func (w *window) moved(_ *glfw.Window, x, y int) {
  246. w.processMoved(x, y)
  247. }
  248. func (w *window) resized(_ *glfw.Window, width, height int) {
  249. w.processResized(width, height)
  250. }
  251. func (w *window) scaled(_ *glfw.Window, x float32, y float32) {
  252. if !isWayland { // other platforms handle this using older APIs
  253. return
  254. }
  255. w.canvas.texScale = x
  256. w.canvas.Refresh(w.canvas.content)
  257. }
  258. func (w *window) frameSized(_ *glfw.Window, width, height int) {
  259. w.processFrameSized(width, height)
  260. }
  261. func (w *window) refresh(_ *glfw.Window) {
  262. w.processRefresh()
  263. }
  264. func (w *window) closed(viewport *glfw.Window) {
  265. if viewport != nil {
  266. viewport.SetShouldClose(false) // reset the closed flag until we check the veto in processClosed
  267. }
  268. w.processClosed()
  269. }
  270. func fyneToNativeCursor(cursor desktop.Cursor) (*glfw.Cursor, bool) {
  271. switch v := cursor.(type) {
  272. case desktop.StandardCursor:
  273. ret, ok := cursorMap[v]
  274. if !ok {
  275. return cursorMap[desktop.DefaultCursor], false
  276. }
  277. return ret, false
  278. default:
  279. img, x, y := cursor.Image()
  280. if img == nil {
  281. return nil, true
  282. }
  283. return glfw.CreateCursor(img, x, y), true
  284. }
  285. }
  286. func (w *window) SetCursor(cursor *glfw.Cursor) {
  287. w.viewport.SetCursor(cursor)
  288. }
  289. func (w *window) setCustomCursor(rawCursor *glfw.Cursor, isCustomCursor bool) {
  290. if w.customCursor != nil {
  291. w.customCursor.Destroy()
  292. w.customCursor = nil
  293. }
  294. if isCustomCursor {
  295. w.customCursor = rawCursor
  296. }
  297. }
  298. func (w *window) mouseMoved(_ *glfw.Window, xpos, ypos float64) {
  299. w.processMouseMoved(xpos, ypos)
  300. }
  301. func (w *window) mouseClicked(_ *glfw.Window, btn glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
  302. button, modifiers := convertMouseButton(btn, mods)
  303. mouseAction := convertAction(action)
  304. w.processMouseClicked(button, mouseAction, modifiers)
  305. }
  306. func (w *window) mouseScrolled(viewport *glfw.Window, xoff float64, yoff float64) {
  307. if runtime.GOOS != "darwin" && xoff == 0 &&
  308. (viewport.GetKey(glfw.KeyLeftShift) == glfw.Press ||
  309. viewport.GetKey(glfw.KeyRightShift) == glfw.Press) {
  310. xoff, yoff = yoff, xoff
  311. }
  312. w.processMouseScrolled(xoff, yoff)
  313. }
  314. func convertMouseButton(btn glfw.MouseButton, mods glfw.ModifierKey) (desktop.MouseButton, fyne.KeyModifier) {
  315. modifier := desktopModifier(mods)
  316. var button desktop.MouseButton
  317. rightClick := false
  318. if runtime.GOOS == "darwin" {
  319. if modifier&fyne.KeyModifierControl != 0 {
  320. rightClick = true
  321. modifier &^= fyne.KeyModifierControl
  322. }
  323. if modifier&fyne.KeyModifierSuper != 0 {
  324. modifier |= fyne.KeyModifierControl
  325. modifier &^= fyne.KeyModifierSuper
  326. }
  327. }
  328. switch btn {
  329. case glfw.MouseButton1:
  330. if rightClick {
  331. button = desktop.MouseButtonSecondary
  332. } else {
  333. button = desktop.MouseButtonPrimary
  334. }
  335. case glfw.MouseButton2:
  336. button = desktop.MouseButtonSecondary
  337. case glfw.MouseButton3:
  338. button = desktop.MouseButtonTertiary
  339. }
  340. return button, modifier
  341. }
  342. //gocyclo:ignore
  343. func glfwKeyToKeyName(key glfw.Key) fyne.KeyName {
  344. switch key {
  345. // numbers - lookup by code to avoid AZERTY using the symbol name instead of number
  346. case glfw.Key0, glfw.KeyKP0:
  347. return fyne.Key0
  348. case glfw.Key1, glfw.KeyKP1:
  349. return fyne.Key1
  350. case glfw.Key2, glfw.KeyKP2:
  351. return fyne.Key2
  352. case glfw.Key3, glfw.KeyKP3:
  353. return fyne.Key3
  354. case glfw.Key4, glfw.KeyKP4:
  355. return fyne.Key4
  356. case glfw.Key5, glfw.KeyKP5:
  357. return fyne.Key5
  358. case glfw.Key6, glfw.KeyKP6:
  359. return fyne.Key6
  360. case glfw.Key7, glfw.KeyKP7:
  361. return fyne.Key7
  362. case glfw.Key8, glfw.KeyKP8:
  363. return fyne.Key8
  364. case glfw.Key9, glfw.KeyKP9:
  365. return fyne.Key9
  366. // non-printable
  367. case glfw.KeyEscape:
  368. return fyne.KeyEscape
  369. case glfw.KeyEnter:
  370. return fyne.KeyReturn
  371. case glfw.KeyTab:
  372. return fyne.KeyTab
  373. case glfw.KeyBackspace:
  374. return fyne.KeyBackspace
  375. case glfw.KeyInsert:
  376. return fyne.KeyInsert
  377. case glfw.KeyDelete:
  378. return fyne.KeyDelete
  379. case glfw.KeyRight:
  380. return fyne.KeyRight
  381. case glfw.KeyLeft:
  382. return fyne.KeyLeft
  383. case glfw.KeyDown:
  384. return fyne.KeyDown
  385. case glfw.KeyUp:
  386. return fyne.KeyUp
  387. case glfw.KeyPageUp:
  388. return fyne.KeyPageUp
  389. case glfw.KeyPageDown:
  390. return fyne.KeyPageDown
  391. case glfw.KeyHome:
  392. return fyne.KeyHome
  393. case glfw.KeyEnd:
  394. return fyne.KeyEnd
  395. case glfw.KeySpace:
  396. return fyne.KeySpace
  397. case glfw.KeyKPEnter:
  398. return fyne.KeyEnter
  399. // desktop
  400. case glfw.KeyLeftShift:
  401. return desktop.KeyShiftLeft
  402. case glfw.KeyRightShift:
  403. return desktop.KeyShiftRight
  404. case glfw.KeyLeftControl:
  405. return desktop.KeyControlLeft
  406. case glfw.KeyRightControl:
  407. return desktop.KeyControlRight
  408. case glfw.KeyLeftAlt:
  409. return desktop.KeyAltLeft
  410. case glfw.KeyRightAlt:
  411. return desktop.KeyAltRight
  412. case glfw.KeyLeftSuper:
  413. return desktop.KeySuperLeft
  414. case glfw.KeyRightSuper:
  415. return desktop.KeySuperRight
  416. case glfw.KeyMenu:
  417. return desktop.KeyMenu
  418. case glfw.KeyPrintScreen:
  419. return desktop.KeyPrintScreen
  420. case glfw.KeyCapsLock:
  421. return desktop.KeyCapsLock
  422. // functions
  423. case glfw.KeyF1:
  424. return fyne.KeyF1
  425. case glfw.KeyF2:
  426. return fyne.KeyF2
  427. case glfw.KeyF3:
  428. return fyne.KeyF3
  429. case glfw.KeyF4:
  430. return fyne.KeyF4
  431. case glfw.KeyF5:
  432. return fyne.KeyF5
  433. case glfw.KeyF6:
  434. return fyne.KeyF6
  435. case glfw.KeyF7:
  436. return fyne.KeyF7
  437. case glfw.KeyF8:
  438. return fyne.KeyF8
  439. case glfw.KeyF9:
  440. return fyne.KeyF9
  441. case glfw.KeyF10:
  442. return fyne.KeyF10
  443. case glfw.KeyF11:
  444. return fyne.KeyF11
  445. case glfw.KeyF12:
  446. return fyne.KeyF12
  447. }
  448. return fyne.KeyUnknown
  449. }
  450. func keyCodeToKeyName(code string) fyne.KeyName {
  451. if len(code) != 1 {
  452. return fyne.KeyUnknown
  453. }
  454. char := code[0]
  455. if char >= 'a' && char <= 'z' {
  456. // Our alphabetical keys are all upper case characters.
  457. return fyne.KeyName('A' + char - 'a')
  458. }
  459. switch char {
  460. case '[':
  461. return fyne.KeyLeftBracket
  462. case '\\':
  463. return fyne.KeyBackslash
  464. case ']':
  465. return fyne.KeyRightBracket
  466. case '\'':
  467. return fyne.KeyApostrophe
  468. case ',':
  469. return fyne.KeyComma
  470. case '-':
  471. return fyne.KeyMinus
  472. case '.':
  473. return fyne.KeyPeriod
  474. case '/':
  475. return fyne.KeySlash
  476. case '*':
  477. return fyne.KeyAsterisk
  478. case '`':
  479. return fyne.KeyBackTick
  480. case ';':
  481. return fyne.KeySemicolon
  482. case '+':
  483. return fyne.KeyPlus
  484. case '=':
  485. return fyne.KeyEqual
  486. }
  487. return fyne.KeyUnknown
  488. }
  489. func keyToName(code glfw.Key, scancode int) fyne.KeyName {
  490. if runtime.GOOS == "darwin" && scancode == 0x69 { // TODO remove once fixed upstream glfw/glfw#1786
  491. code = glfw.KeyPrintScreen
  492. }
  493. ret := glfwKeyToKeyName(code)
  494. if ret != fyne.KeyUnknown {
  495. return ret
  496. }
  497. keyName := glfw.GetKeyName(code, scancode)
  498. return keyCodeToKeyName(keyName)
  499. }
  500. func convertAction(action glfw.Action) action {
  501. switch action {
  502. case glfw.Press:
  503. return press
  504. case glfw.Release:
  505. return release
  506. case glfw.Repeat:
  507. return repeat
  508. }
  509. panic("Could not convert glfw.Action.")
  510. }
  511. func convertASCII(key glfw.Key) fyne.KeyName {
  512. if key < glfw.KeyA || key > glfw.KeyZ {
  513. return fyne.KeyUnknown
  514. }
  515. return fyne.KeyName(rune(key))
  516. }
  517. func (w *window) keyPressed(_ *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
  518. keyName := keyToName(key, scancode)
  519. keyDesktopModifier := desktopModifier(mods)
  520. keyAction := convertAction(action)
  521. keyASCII := convertASCII(key)
  522. w.processKeyPressed(keyName, keyASCII, scancode, keyAction, keyDesktopModifier)
  523. }
  524. func desktopModifier(mods glfw.ModifierKey) fyne.KeyModifier {
  525. var m fyne.KeyModifier
  526. if (mods & glfw.ModShift) != 0 {
  527. m |= fyne.KeyModifierShift
  528. }
  529. if (mods & glfw.ModControl) != 0 {
  530. m |= fyne.KeyModifierControl
  531. }
  532. if (mods & glfw.ModAlt) != 0 {
  533. m |= fyne.KeyModifierAlt
  534. }
  535. if (mods & glfw.ModSuper) != 0 {
  536. m |= fyne.KeyModifierSuper
  537. }
  538. return m
  539. }
  540. // charInput defines the character with modifiers callback which is called when a
  541. // Unicode character is input.
  542. //
  543. // Characters do not map 1:1 to physical keys, as a key may produce zero, one or more characters.
  544. func (w *window) charInput(viewport *glfw.Window, char rune) {
  545. w.processCharInput(char)
  546. }
  547. func (w *window) focused(_ *glfw.Window, focused bool) {
  548. w.processFocused(focused)
  549. }
  550. func (w *window) DetachCurrentContext() {
  551. glfw.DetachCurrentContext()
  552. }
  553. func (w *window) rescaleOnMain() {
  554. if w.isClosing() {
  555. return
  556. }
  557. w.fitContent()
  558. if w.fullScreen {
  559. w.width, w.height = w.viewport.GetSize()
  560. scaledFull := fyne.NewSize(
  561. internal.UnscaleInt(w.canvas, w.width),
  562. internal.UnscaleInt(w.canvas, w.height))
  563. w.canvas.Resize(scaledFull)
  564. return
  565. }
  566. size := w.canvas.size.Max(w.canvas.MinSize())
  567. newWidth, newHeight := w.screenSize(size)
  568. w.viewport.SetSize(newWidth, newHeight)
  569. }
  570. func (w *window) create() {
  571. runOnMain(func() {
  572. if !isWayland {
  573. // make the window hidden, we will set it up and then show it later
  574. glfw.WindowHint(glfw.Visible, glfw.False)
  575. }
  576. if w.decorate {
  577. glfw.WindowHint(glfw.Decorated, glfw.True)
  578. } else {
  579. glfw.WindowHint(glfw.Decorated, glfw.False)
  580. }
  581. if w.fixedSize {
  582. glfw.WindowHint(glfw.Resizable, glfw.False)
  583. } else {
  584. glfw.WindowHint(glfw.Resizable, glfw.True)
  585. }
  586. glfw.WindowHint(glfw.AutoIconify, glfw.False)
  587. initWindowHints()
  588. pixWidth, pixHeight := w.screenSize(w.canvas.size)
  589. pixWidth = int(fyne.Max(float32(pixWidth), float32(w.width)))
  590. if pixWidth == 0 {
  591. pixWidth = 10
  592. }
  593. pixHeight = int(fyne.Max(float32(pixHeight), float32(w.height)))
  594. if pixHeight == 0 {
  595. pixHeight = 10
  596. }
  597. win, err := glfw.CreateWindow(pixWidth, pixHeight, w.title, nil, nil)
  598. if err != nil {
  599. w.driver.initFailed("window creation error", err)
  600. return
  601. }
  602. w.viewLock.Lock()
  603. w.viewport = win
  604. w.viewLock.Unlock()
  605. })
  606. if w.view() == nil { // something went wrong above, it will have been logged
  607. return
  608. }
  609. // run the GL init on the draw thread
  610. runOnDraw(w, func() {
  611. w.canvas.SetPainter(gl.NewPainter(w.canvas, w))
  612. w.canvas.Painter().Init()
  613. })
  614. runOnMain(func() {
  615. w.setDarkMode()
  616. win := w.view()
  617. win.SetCloseCallback(w.closed)
  618. win.SetPosCallback(w.moved)
  619. win.SetSizeCallback(w.resized)
  620. win.SetFramebufferSizeCallback(w.frameSized)
  621. win.SetRefreshCallback(w.refresh)
  622. win.SetContentScaleCallback(w.scaled)
  623. win.SetCursorPosCallback(w.mouseMoved)
  624. win.SetMouseButtonCallback(w.mouseClicked)
  625. win.SetScrollCallback(w.mouseScrolled)
  626. win.SetKeyCallback(w.keyPressed)
  627. win.SetCharCallback(w.charInput)
  628. win.SetFocusCallback(w.focused)
  629. w.canvas.detectedScale = w.detectScale()
  630. w.canvas.scale = w.calculatedScale()
  631. w.canvas.texScale = w.detectTextureScale()
  632. // update window size now we have scaled detected
  633. w.fitContent()
  634. for _, fn := range w.pending {
  635. fn()
  636. }
  637. if w.FixedSize() && (w.requestedWidth == 0 || w.requestedHeight == 0) {
  638. bigEnough := w.canvas.canvasSize(w.canvas.Content().MinSize())
  639. w.width, w.height = internal.ScaleInt(w.canvas, bigEnough.Width), internal.ScaleInt(w.canvas, bigEnough.Height)
  640. w.shouldWidth, w.shouldHeight = w.width, w.height
  641. }
  642. w.requestedWidth, w.requestedHeight = w.width, w.height
  643. // order of operation matters so we do these last items in order
  644. w.viewport.SetSize(w.shouldWidth, w.shouldHeight) // ensure we requested latest size
  645. })
  646. }
  647. func (w *window) view() *glfw.Window {
  648. w.viewLock.RLock()
  649. defer w.viewLock.RUnlock()
  650. if w.closing {
  651. return nil
  652. }
  653. return w.viewport
  654. }