window_desktop.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  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. viewport.SetShouldClose(false) // reset the closed flag until we check the veto in processClosed
  266. w.processClosed()
  267. }
  268. func fyneToNativeCursor(cursor desktop.Cursor) (*glfw.Cursor, bool) {
  269. switch v := cursor.(type) {
  270. case desktop.StandardCursor:
  271. ret, ok := cursorMap[v]
  272. if !ok {
  273. return cursorMap[desktop.DefaultCursor], false
  274. }
  275. return ret, false
  276. default:
  277. img, x, y := cursor.Image()
  278. if img == nil {
  279. return nil, true
  280. }
  281. return glfw.CreateCursor(img, x, y), true
  282. }
  283. }
  284. func (w *window) SetCursor(cursor *glfw.Cursor) {
  285. w.viewport.SetCursor(cursor)
  286. }
  287. func (w *window) setCustomCursor(rawCursor *glfw.Cursor, isCustomCursor bool) {
  288. if w.customCursor != nil {
  289. w.customCursor.Destroy()
  290. w.customCursor = nil
  291. }
  292. if isCustomCursor {
  293. w.customCursor = rawCursor
  294. }
  295. }
  296. func (w *window) mouseMoved(_ *glfw.Window, xpos, ypos float64) {
  297. w.processMouseMoved(xpos, ypos)
  298. }
  299. func (w *window) mouseClicked(_ *glfw.Window, btn glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
  300. button, modifiers := convertMouseButton(btn, mods)
  301. mouseAction := convertAction(action)
  302. w.processMouseClicked(button, mouseAction, modifiers)
  303. }
  304. func (w *window) mouseScrolled(viewport *glfw.Window, xoff float64, yoff float64) {
  305. if runtime.GOOS != "darwin" && xoff == 0 &&
  306. (viewport.GetKey(glfw.KeyLeftShift) == glfw.Press ||
  307. viewport.GetKey(glfw.KeyRightShift) == glfw.Press) {
  308. xoff, yoff = yoff, xoff
  309. }
  310. w.processMouseScrolled(xoff, yoff)
  311. }
  312. func convertMouseButton(btn glfw.MouseButton, mods glfw.ModifierKey) (desktop.MouseButton, fyne.KeyModifier) {
  313. modifier := desktopModifier(mods)
  314. var button desktop.MouseButton
  315. rightClick := false
  316. if runtime.GOOS == "darwin" {
  317. if modifier&fyne.KeyModifierControl != 0 {
  318. rightClick = true
  319. modifier &^= fyne.KeyModifierControl
  320. }
  321. if modifier&fyne.KeyModifierSuper != 0 {
  322. modifier |= fyne.KeyModifierControl
  323. modifier &^= fyne.KeyModifierSuper
  324. }
  325. }
  326. switch btn {
  327. case glfw.MouseButton1:
  328. if rightClick {
  329. button = desktop.MouseButtonSecondary
  330. } else {
  331. button = desktop.MouseButtonPrimary
  332. }
  333. case glfw.MouseButton2:
  334. button = desktop.MouseButtonSecondary
  335. case glfw.MouseButton3:
  336. button = desktop.MouseButtonTertiary
  337. }
  338. return button, modifier
  339. }
  340. //gocyclo:ignore
  341. func glfwKeyToKeyName(key glfw.Key) fyne.KeyName {
  342. switch key {
  343. // numbers - lookup by code to avoid AZERTY using the symbol name instead of number
  344. case glfw.Key0, glfw.KeyKP0:
  345. return fyne.Key0
  346. case glfw.Key1, glfw.KeyKP1:
  347. return fyne.Key1
  348. case glfw.Key2, glfw.KeyKP2:
  349. return fyne.Key2
  350. case glfw.Key3, glfw.KeyKP3:
  351. return fyne.Key3
  352. case glfw.Key4, glfw.KeyKP4:
  353. return fyne.Key4
  354. case glfw.Key5, glfw.KeyKP5:
  355. return fyne.Key5
  356. case glfw.Key6, glfw.KeyKP6:
  357. return fyne.Key6
  358. case glfw.Key7, glfw.KeyKP7:
  359. return fyne.Key7
  360. case glfw.Key8, glfw.KeyKP8:
  361. return fyne.Key8
  362. case glfw.Key9, glfw.KeyKP9:
  363. return fyne.Key9
  364. // non-printable
  365. case glfw.KeyEscape:
  366. return fyne.KeyEscape
  367. case glfw.KeyEnter:
  368. return fyne.KeyReturn
  369. case glfw.KeyTab:
  370. return fyne.KeyTab
  371. case glfw.KeyBackspace:
  372. return fyne.KeyBackspace
  373. case glfw.KeyInsert:
  374. return fyne.KeyInsert
  375. case glfw.KeyDelete:
  376. return fyne.KeyDelete
  377. case glfw.KeyRight:
  378. return fyne.KeyRight
  379. case glfw.KeyLeft:
  380. return fyne.KeyLeft
  381. case glfw.KeyDown:
  382. return fyne.KeyDown
  383. case glfw.KeyUp:
  384. return fyne.KeyUp
  385. case glfw.KeyPageUp:
  386. return fyne.KeyPageUp
  387. case glfw.KeyPageDown:
  388. return fyne.KeyPageDown
  389. case glfw.KeyHome:
  390. return fyne.KeyHome
  391. case glfw.KeyEnd:
  392. return fyne.KeyEnd
  393. case glfw.KeySpace:
  394. return fyne.KeySpace
  395. case glfw.KeyKPEnter:
  396. return fyne.KeyEnter
  397. // desktop
  398. case glfw.KeyLeftShift:
  399. return desktop.KeyShiftLeft
  400. case glfw.KeyRightShift:
  401. return desktop.KeyShiftRight
  402. case glfw.KeyLeftControl:
  403. return desktop.KeyControlLeft
  404. case glfw.KeyRightControl:
  405. return desktop.KeyControlRight
  406. case glfw.KeyLeftAlt:
  407. return desktop.KeyAltLeft
  408. case glfw.KeyRightAlt:
  409. return desktop.KeyAltRight
  410. case glfw.KeyLeftSuper:
  411. return desktop.KeySuperLeft
  412. case glfw.KeyRightSuper:
  413. return desktop.KeySuperRight
  414. case glfw.KeyMenu:
  415. return desktop.KeyMenu
  416. case glfw.KeyPrintScreen:
  417. return desktop.KeyPrintScreen
  418. case glfw.KeyCapsLock:
  419. return desktop.KeyCapsLock
  420. // functions
  421. case glfw.KeyF1:
  422. return fyne.KeyF1
  423. case glfw.KeyF2:
  424. return fyne.KeyF2
  425. case glfw.KeyF3:
  426. return fyne.KeyF3
  427. case glfw.KeyF4:
  428. return fyne.KeyF4
  429. case glfw.KeyF5:
  430. return fyne.KeyF5
  431. case glfw.KeyF6:
  432. return fyne.KeyF6
  433. case glfw.KeyF7:
  434. return fyne.KeyF7
  435. case glfw.KeyF8:
  436. return fyne.KeyF8
  437. case glfw.KeyF9:
  438. return fyne.KeyF9
  439. case glfw.KeyF10:
  440. return fyne.KeyF10
  441. case glfw.KeyF11:
  442. return fyne.KeyF11
  443. case glfw.KeyF12:
  444. return fyne.KeyF12
  445. }
  446. return fyne.KeyUnknown
  447. }
  448. func keyCodeToKeyName(code string) fyne.KeyName {
  449. if len(code) != 1 {
  450. return fyne.KeyUnknown
  451. }
  452. char := code[0]
  453. if char >= 'a' && char <= 'z' {
  454. // Our alphabetical keys are all upper case characters.
  455. return fyne.KeyName('A' + char - 'a')
  456. }
  457. switch char {
  458. case '[':
  459. return fyne.KeyLeftBracket
  460. case '\\':
  461. return fyne.KeyBackslash
  462. case ']':
  463. return fyne.KeyRightBracket
  464. case '\'':
  465. return fyne.KeyApostrophe
  466. case ',':
  467. return fyne.KeyComma
  468. case '-':
  469. return fyne.KeyMinus
  470. case '.':
  471. return fyne.KeyPeriod
  472. case '/':
  473. return fyne.KeySlash
  474. case '*':
  475. return fyne.KeyAsterisk
  476. case '`':
  477. return fyne.KeyBackTick
  478. case ';':
  479. return fyne.KeySemicolon
  480. case '+':
  481. return fyne.KeyPlus
  482. case '=':
  483. return fyne.KeyEqual
  484. }
  485. return fyne.KeyUnknown
  486. }
  487. func keyToName(code glfw.Key, scancode int) fyne.KeyName {
  488. if runtime.GOOS == "darwin" && scancode == 0x69 { // TODO remove once fixed upstream glfw/glfw#1786
  489. code = glfw.KeyPrintScreen
  490. }
  491. ret := glfwKeyToKeyName(code)
  492. if ret != fyne.KeyUnknown {
  493. return ret
  494. }
  495. keyName := glfw.GetKeyName(code, scancode)
  496. return keyCodeToKeyName(keyName)
  497. }
  498. func convertAction(action glfw.Action) action {
  499. switch action {
  500. case glfw.Press:
  501. return press
  502. case glfw.Release:
  503. return release
  504. case glfw.Repeat:
  505. return repeat
  506. }
  507. panic("Could not convert glfw.Action.")
  508. }
  509. func convertASCII(key glfw.Key) fyne.KeyName {
  510. if key < glfw.KeyA || key > glfw.KeyZ {
  511. return fyne.KeyUnknown
  512. }
  513. return fyne.KeyName(rune(key))
  514. }
  515. func (w *window) keyPressed(_ *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
  516. keyName := keyToName(key, scancode)
  517. keyDesktopModifier := desktopModifier(mods)
  518. keyAction := convertAction(action)
  519. keyASCII := convertASCII(key)
  520. w.processKeyPressed(keyName, keyASCII, scancode, keyAction, keyDesktopModifier)
  521. }
  522. func desktopModifier(mods glfw.ModifierKey) fyne.KeyModifier {
  523. var m fyne.KeyModifier
  524. if (mods & glfw.ModShift) != 0 {
  525. m |= fyne.KeyModifierShift
  526. }
  527. if (mods & glfw.ModControl) != 0 {
  528. m |= fyne.KeyModifierControl
  529. }
  530. if (mods & glfw.ModAlt) != 0 {
  531. m |= fyne.KeyModifierAlt
  532. }
  533. if (mods & glfw.ModSuper) != 0 {
  534. m |= fyne.KeyModifierSuper
  535. }
  536. return m
  537. }
  538. // charInput defines the character with modifiers callback which is called when a
  539. // Unicode character is input.
  540. //
  541. // Characters do not map 1:1 to physical keys, as a key may produce zero, one or more characters.
  542. func (w *window) charInput(viewport *glfw.Window, char rune) {
  543. w.processCharInput(char)
  544. }
  545. func (w *window) focused(_ *glfw.Window, focused bool) {
  546. w.processFocused(focused)
  547. }
  548. func (w *window) DetachCurrentContext() {
  549. glfw.DetachCurrentContext()
  550. }
  551. func (w *window) rescaleOnMain() {
  552. if w.isClosing() {
  553. return
  554. }
  555. w.fitContent()
  556. if w.fullScreen {
  557. w.width, w.height = w.viewport.GetSize()
  558. scaledFull := fyne.NewSize(
  559. internal.UnscaleInt(w.canvas, w.width),
  560. internal.UnscaleInt(w.canvas, w.height))
  561. w.canvas.Resize(scaledFull)
  562. return
  563. }
  564. size := w.canvas.size.Max(w.canvas.MinSize())
  565. newWidth, newHeight := w.screenSize(size)
  566. w.viewport.SetSize(newWidth, newHeight)
  567. }
  568. func (w *window) create() {
  569. runOnMain(func() {
  570. if !isWayland {
  571. // make the window hidden, we will set it up and then show it later
  572. glfw.WindowHint(glfw.Visible, glfw.False)
  573. }
  574. if w.decorate {
  575. glfw.WindowHint(glfw.Decorated, glfw.True)
  576. } else {
  577. glfw.WindowHint(glfw.Decorated, glfw.False)
  578. }
  579. if w.fixedSize {
  580. glfw.WindowHint(glfw.Resizable, glfw.False)
  581. } else {
  582. glfw.WindowHint(glfw.Resizable, glfw.True)
  583. }
  584. glfw.WindowHint(glfw.AutoIconify, glfw.False)
  585. initWindowHints()
  586. pixWidth, pixHeight := w.screenSize(w.canvas.size)
  587. pixWidth = int(fyne.Max(float32(pixWidth), float32(w.width)))
  588. if pixWidth == 0 {
  589. pixWidth = 10
  590. }
  591. pixHeight = int(fyne.Max(float32(pixHeight), float32(w.height)))
  592. if pixHeight == 0 {
  593. pixHeight = 10
  594. }
  595. win, err := glfw.CreateWindow(pixWidth, pixHeight, w.title, nil, nil)
  596. if err != nil {
  597. w.driver.initFailed("window creation error", err)
  598. return
  599. }
  600. w.viewLock.Lock()
  601. w.viewport = win
  602. w.viewLock.Unlock()
  603. })
  604. if w.view() == nil { // something went wrong above, it will have been logged
  605. return
  606. }
  607. // run the GL init on the draw thread
  608. runOnDraw(w, func() {
  609. w.canvas.SetPainter(gl.NewPainter(w.canvas, w))
  610. w.canvas.Painter().Init()
  611. })
  612. runOnMain(func() {
  613. w.setDarkMode()
  614. win := w.view()
  615. win.SetCloseCallback(w.closed)
  616. win.SetPosCallback(w.moved)
  617. win.SetSizeCallback(w.resized)
  618. win.SetFramebufferSizeCallback(w.frameSized)
  619. win.SetRefreshCallback(w.refresh)
  620. win.SetContentScaleCallback(w.scaled)
  621. win.SetCursorPosCallback(w.mouseMoved)
  622. win.SetMouseButtonCallback(w.mouseClicked)
  623. win.SetScrollCallback(w.mouseScrolled)
  624. win.SetKeyCallback(w.keyPressed)
  625. win.SetCharCallback(w.charInput)
  626. win.SetFocusCallback(w.focused)
  627. w.canvas.detectedScale = w.detectScale()
  628. w.canvas.scale = w.calculatedScale()
  629. w.canvas.texScale = w.detectTextureScale()
  630. // update window size now we have scaled detected
  631. w.fitContent()
  632. for _, fn := range w.pending {
  633. fn()
  634. }
  635. if w.FixedSize() && (w.requestedWidth == 0 || w.requestedHeight == 0) {
  636. bigEnough := w.canvas.canvasSize(w.canvas.Content().MinSize())
  637. w.width, w.height = internal.ScaleInt(w.canvas, bigEnough.Width), internal.ScaleInt(w.canvas, bigEnough.Height)
  638. w.shouldWidth, w.shouldHeight = w.width, w.height
  639. }
  640. w.requestedWidth, w.requestedHeight = w.width, w.height
  641. // order of operation matters so we do these last items in order
  642. w.viewport.SetSize(w.shouldWidth, w.shouldHeight) // ensure we requested latest size
  643. })
  644. }
  645. func (w *window) view() *glfw.Window {
  646. w.viewLock.RLock()
  647. defer w.viewLock.RUnlock()
  648. if w.closing {
  649. return nil
  650. }
  651. return w.viewport
  652. }